Fork me on GitHub

代理模式一:动态代理

定义

为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。

其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

静态代理

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

动态代理

利用反射原理,Proxy动态生成被代理对象,实现InvocationHandler接口,利用其中的invoke方法执行目标类的方法。

基于接口的动态代理

简介

提供者: JDK官方的Proxy类;
要求: 被代理类最少实现一个接口。

代码
  • 定义一个接口
public interface ProxyTargetInterface {

    public Integer add(Integer a,Integer b);
}
  • 定义一个类实现该接口
public class ProxyTargetImpl implements ProxyTargetInterface{


    public Integer add(Integer a,Integer b){
        System.out.println("a+b的输出值为:"+(a+b));
        return a+b;
    }

}
  • 定义一个代理类
public class ProxyHandler {

    public Object proxy(Object object){

        Object proxyInstance = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理之前-----");
                //进行执行代理方法
                Object invoke = method.invoke(object, args);
                System.out.println("代理之后-----");
                return invoke;
            }
        });

        return proxyInstance;
    }
}
  • 进行测试
    @Test
    public void test() {
        ProxyTargetImpl target=new ProxyTargetImpl();
        ProxyHandler handler=new ProxyHandler();
        ProxyTargetInterface proxy =(ProxyTargetInterface) handler.proxy(target);
        Integer add = proxy.add(1, 2);
        System.out.println(add);
    }

基于子类的动态代理

简介

提供者: 第三方的GGLib,如果报asmxxxx异常,需要导入asm.jar;
要求: 被代理类不能用final修饰的类(最早类)。

代码
  • 定义一个普通类
public class EnhancerTargetImpl {

    public String basicAct(Float money){
        System.out.println("开始基本的表演"+money);
        return null;
    }

    public String dangerAct(Float money){
        System.out.println("开始危险的表演"+money);
        return null;
    }
  • 定义一个代理类
public Object enhancer(Object object){
        Object o = Enhancer.create(object.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Float money = (Float) objects[0];
                String name = method.getName();
                Object invoke = null;
                System.out.println("代理之前-----");
                if ("basicAct".equals(name)) {
                    invoke = method.invoke(object, money / 3);
                }
                if ("dangerAct".equals(name)) {
                    invoke = method.invoke(object, money / 2);
                }
                System.out.println("代理之后-----");
                return invoke;
            }
        });
        return o;
    }
  • 进行测试
   @Test
    public void test() {
        EnhancerTargetImpl target=new EnhancerTargetImpl();
        EnhancerHandler handler=new EnhancerHandler();
        EnhancerTargetImpl targetImpl=(EnhancerTargetImpl)handler.enhancer(target);
        targetImpl.basicAct(2000F);
        targetImpl.dangerAct(5000F);
    }

GGLib代理和JDK代理的区别

  • JDK代理: 采用的Proxy.newProxyInstance() 使用的是InvocationHandler()方法。
  • GGLib代理: 采用的是Enhancer.create() 使用的是MethodInterceptor()方法。
  • JDK动态代理是面向接口的。
  • CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
  • 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
  • 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
  • JDK动态代理的速度比CGLib快。

Gitee代码地址

Proxy代理:https://gitee.com/zhuayng/foundation-study/blob/develop/JavaBasis/DesignModel/src/main/java/com/yxkj/designmodel/modular/proxytarget/agent/ProxyHandler.java
Enhancer代理:https://gitee.com/zhuayng/foundation-study/blob/develop/JavaBasis/DesignModel/src/main/java/com/yxkj/designmodel/modular/proxytarget/agent/EnhancerHandler.java

思考

1:动态代理为啥必须要实现接口
2:为啥不能method.invoke(proxy, args)这样编码,那Object proxy有何作用。

参考:https://www.jianshu.com/p/9bcac608c714
https://www.zhihu.com/question/20794107

posted @ 2021-06-19 17:18  晨度  阅读(37)  评论(0编辑  收藏  举报