代理模式详解

2020-02-05 16:04
1015
0
为其他对象提供一种代理以控制对这个对象的访问。
 
一、何时使用
1、有时不能直接给对方访问时可以通过代理模式
2、代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
 
二、应用场景
SpringAop日志收集、权限控制、过滤器RPC远程调用
 
三、实现方式
代理模式如上图主要包含三个角色:
抽象主题角色(Subject):可以是接口,也可以是抽象类;
委托类角色(RealSubject):真实主题角色,业务逻辑的具体执行者;
代理类角色(Proxy):内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
 
代理模式实现方式:静态代理和动态代理
代理类的实现方式:继承被代理类(CGLIB通过继承形式的)、实现接口(jdk动态代理用的此方法)
 
四、静态代理
      继承的方式:
public class OrderServiceProxy extends OrderServiceImpl {

    public void order() {
        System.out.println(">>>打印订单日志开始");
        super.order();// 执行父类的order 方法OrderServiceImpl
        System.out.println(">>>打印订单日志结束");
    }
    
}

    可以直接通过如下方式调用:

OrderService orderService = new OrderServiceProxy();
orderService.order();

  实现接口的方式:

public class OrderServiceProxy implements OrderService {
    /**
     * 被代理对象
     */
    private OrderService orderService;

    public void order() {
        System.out.println(">>>打印订单日志开始");
        orderService.order();
        System.out.println(">>>打印订单日志结束");
    }

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

}

可以直接通过如下方式调用:

OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
orderService.order();

 

五、动态代理

Jdk动态代理是通过反射
CGLIB动态代理通过字节码技术
 
jdk动态代理:
实现InvocationHandler接口,自动生成的代理类的构造函数会传入此类的对象,调用其invoke方法。
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 被代理类对象 目标代理对象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>jdk打印日志开始:proxy:"+proxy.getClass().toString());
        Object reuslt = method.invoke(target, args);// java的反射机制执行方法 执行目标对象的方法
        System.out.println(">>>jdk打印日志结束");
        return reuslt;
    }

    /**
     * 使用jdk动态代理创建代理类
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

使用jdk动态代理调用的方式:

//将JDK动态代理生成的class文件保存到本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//使用jdk动态代理
OrderService proxy = new JdkInvocationHandler(new OrderServiceImpl()).getProxy();
proxy.order();

(此处顺便外链下JDK动态代理生成class文件失败的问题:https://blog.csdn.net/zyq8514700/article/details/99892329)

调用JDK动态代理的时候,不能赋值给实现类,如下图,只能通过父类接收。所以说jdk动态代理要实现接口才可以。

这是为何?可以反编译看一下自动生成的代理类,因为其集成的Proxy类,实现的OrderService接口,因此只能通过父类接收。

CGLIB实现动态代理

CGLIB通过ASM字节码处理框架实现的动态代理
Jdk动态代理要实现接口才可以,CGLIB里被代理类就不需要实现接口,因为是通过的继承。
首先继承MethodInterceptor:

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(">>>>cglib日志收集开始....");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println(">>>>cglib日志收集结束....");
        return reuslt;
    }
}

使用动态代理:

//将动态代理生成的class文件保存到本地
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
// 使用cglib动态代理
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(cglibMethodInterceptor);
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
orderServiceImpl.order();

用此方式OrderServiceImpl无需实现OrderService接口。

 

参考文献:
https://www.runoob.com/design-pattern/strategy-pattern.html

http://www.mayikt.com/front/couinfo/194/0#

全部评论