深入理解动态代理源码

前言:  早期学习了动态代理在实际开发中的使用场景和使用方法,我们也知道了最经典的mybatis的mapper就是采用动态代理来实现的,那么动态代理的背后是怎样的原理?为什么能实现动态代理?为什么动态代理只可以代理接口,而无法代理普通类?为什么动态代理需要传入类的classLoder和接口?带着这些疑问,我们来开启本期的主题:探究动态代理的内部原理。

本篇博客的目录

一:动态代理的基本使用方法

二:动态代理的内部运行过程

三:几个相关的问题

四:总结

一:动态代理的基本使用方法

1.1:简单例子

首先我们来模拟一个简单的动态代理的过程:某歌手去参加一个晚会,需要唱歌,他在演奏的过程中需要别人来报幕:演奏开始、演奏结束,每个歌手都遵循这样的过程,在歌手进行表演的过程,穿插着主持人的开场白和结语,我们来用代码模拟这个场景:

1.2:代理接口

首先我们来定义一个singer接口表示我们将要代理的接口:

public interface Singer {
    /**
    * 表演
    * @param soonName
    */
    public void perform(String soonName);
}

1.3:接口的具体实现类

public class Jay implements Singer {
 
    public void perform(String soonName) {
        System.out.println("接下来我为大家唱一首"+soonName);
    }
}

1.4:辅助类,用来模拟注册人的前后台词

public class Presenter  {

public void before(){
        System.out.println("请开始你的表演!");
    }

public void after(){
        System.out.println("表演结束,大家鼓掌!");
    }
}

1.5:具体的代理类

这里用proxy.newProxyInstance来创建一个代理类,传入原始类的类加载器和接口与接口InvocationHandler,同时插入Presenter类的before与after方法,用于前置和后置处理

public class SingerProxy {

private Presenter presenter;

public SingerProxy(Presenter presenter){
      this.presenter = presenter;
  }
    /**
    * 获取代理对象
    * @return
    */
    public Singer getProxy(){

final Singer jay = new Jay();
        Singer singerProxy = (Singer)Proxy.newProxyInstance(jay.getClass().getClassLoader(), jay.getClass().getInterfaces(), new InvocationHandler() {」
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                presenter.before();
                method.invoke(jay, args);
                presenter.after();
                return null;
            }
        });
        return singerProxy;
    }
}

1.6:测试类

public class Test {
    public static void main(String[] args) {
        SingerProxy singerProxy = new SingerProxy(new Presenter());
        Singer proxy = singerProxy.getProxy();
        proxy.perform("《夜曲》");
    }
}

输出:

深入理解动态代理源码

二:动态代理的内部探究

从上面的例子可以看出我们首先生成了一个代理类,然后用代理类来调用原始接口的方法,就可以实现我们的预设的逻辑,在原始接口的前后(或者出现异常的时候)插入我们想要的逻辑,那么究竟是为什么呢?

2.1:找到生成的代理类

我们如果需要打开生成的类,首先需要在测试类中添加这行代码,设置系统属性来保存生成的代理类的class文件:

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

2.2:singerProxy类

通过动态代理生成的代理类名为:$Proxy0.class然后通过intelj idea反编译之后源代码是这样的,这里主要看到有4个方法,method的m1\m2\m3\m0;分别由反射获取的equals()、toString()、perform()、hashcode()方法,同时代理类继承了proxy并且实现了原始Singer接口,重写了perform()方法,所以这就解释了为什么代理类可以调用perform()方法,在perform方法中,又调用了父类中的InvoationHander的invoke方法,并且传入原始接口中的方法,而invoke方法在我们在创建代理类的时候重写过,所以就会按照我们自定义的逻辑调用invoke方法,按照顺序执行

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/134460161610747b2b7b7e91a07b672f.html