Jdk的动态代理是在方法调用期间来动态生成代理的字节码类,然后进行方法调用,比如本身方法是save()方法,然后实际调用save方法的时候不会直接调用save方法,而是先去调用一个代理方法save,然后通过代理方法去调用代理监控类的invoke方法,然后我们就可以在invoke方法中再去调用实际的save方法,并且可以定制个性化的需求.
jdk提供了如何实现动态代理的方法,有几个要求,
1.建立一个代理的监控类这个类必须实现InvocationHandler接口,重写他的invoke方法,在invoke方法中进行个性化定制和实际方法的调用
2.要想被代理,那被代理对象必须实现接口,jdk动态代理是基于接口来代理的,所以实现类中自己特色的方法是没办法使用jdk动态代理增强的
3.jdk提供了一个工具类来生成代理对象Proxy,提供了一个静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)来帮忙快速创建代理类,
参数1:被代理类加载的时候用的类加载器(用来加载动态创建的代理类的字节码)
参数2:被代理类实现的所有接口,要给动态创建的代理类的字节码实现这些接口,并且作为标识可以判断是否已经缓存过实现这些接口的代理类避免重复创建
参数3:自定义的代理的监控类,会作为构造函数的参数,赋值给新创建的代理类字节码生成对象的成员变量InvocationHandler h;
大致理解下逻辑,Proxy类调用newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
1.首先根据传进来的类加载器,所有的接口[参数1,参数2]来得到最终的代理对象的Class对象,在这里会实现传进来的接口中的所有方法[Look up or generate the designated proxy class.]
2.然后获取代理对象的Class的的构造方法,并且是带有一个参数类型是{ InvocationHandler.class }的构造方法Constructor对象
然后调用Constructor的newInstance(new Object[] { h });方法创建出代理对象类型为Object类型
3.然后在初始化代理对象时候通过静态代码块中反射的方式Class.forName("xxx").getMethod("save", new Class[0])把真实的方法获取到,作为成员变量保存
4.然后调用代理对象中实现了接口的方法save,在save方法中调用成员变量InvocationHandler h的invoke方法,这样就进入了我们自定义的代理类的监控类[实现了InvocationHandler接口]中实现的invoke方法
5.至此整个执行流程结束,完成了代理方法的调用,在代理方法中调用个性化的方法并且也调用了被代理对象的方法本身
示例如下:
Jdk1.6中的实现
核心方法就是如何创建的代理对象,调用的是Proxy类的静态方法newInstance方法,实现如下:
其中的重点是如何根据类加载器,实现的接口得到代理对象的Class对象,实现方法在getProxyClass(loader, interfaces)中,具体如下:
上面创建代理类的Class中核心方法是ProxyGenerator.generateProxyClass(proxyName, interfaces);具体的实现可以点进去看,实际又去调用了ProxyGenerator.generateClassFile()方法,而且有一个成员属性可以设置是否把生成的代理类字节码保存到硬盘上属性名为saveGeneratedFiles,设置方式如下:
可以看下生成的class,然后反编译之后
反编译之后如下:
上面就是一个对象被代理的整个过程了.
大致理解下逻辑,Proxy类调用newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
1.首先根据传进来的类加载器,所有的接口[参数1,参数2]来得到最终的代理对象的Class对象,在这里会实现传进来的接口中的所有方法[Look up or generate the designated proxy class.]
2.然后获取代理对象的Class的的构造方法,并且是带有一个参数类型是{ InvocationHandler.class }的构造方法Constructor对象
然后调用Constructor的newInstance(new Object[] { h });方法创建出代理对象类型为Object类型
3.然后在初始化代理对象时候通过静态代码块中反射的方式Class.forName("xxx").getMethod("save", new Class[0])把真实的方法获取到,作为成员变量保存
4.然后调用代理对象中实现了接口的方法save,在save方法中调用成员变量InvocationHandler h的invoke方法,这样就进入了我们自定义的代理类的监控类[实现了InvocationHandler接口]中实现的invoke方法
5.至此整个执行流程结束,完成了代理方法的调用,在代理方法中调用个性化的方法并且也调用了被代理对象的方法本身
Spring中的具体的jdk基于接口方式的动态代理的监控类是JdkDynamicAopProxy,他里边肯定实现了invoke方法,并且实现了InvocationHandler接口,并且提供了getProxy方法,
Jdk1.7中的实现
1.7中同样的核心入口方法也是Proxy.newInstance(),实现如下
上面的重点是如何获取代理对象的Class对象,在jdk1.6中的方法名是getProxyClass(loader,interfaces),1.7中进行了封装在getProxyClass0(loader, intfs)中,具体实现如下:
上面的缓存是proxyClassCache,定义的是一个WeakCache类型的对象,如下:
map是实现缓存的核心变量,是一个嵌套结构(key,sub-key) -> value key是传入的ClassLoader包装后的对象,sub-key是WeakCache构造函数传入的的KeyFactory()生成的,value就是生成的代理类对象是由WeakCache传入的构造函数ProxyClassFactory生成的,生成sub-key的KeyFactory如下:
Value是传入的ProxyClassFactory生成的,对应的apply方法其实就是jdk1.6中的getProxyClass方法,如下:
通过sub-key拿到一个Supplier<Class<?>>对象,然后调用get方法得到代理类的Class对象,然后看proxyClassCache.get(loader,interfaces)方法如下:
通过sub-key得到supplier,supplier就是这个factory调用他的get方法如下:
1.7没有1.6的逻辑好理顺,但是思路都是一样的,缓存中获取,有了就返回,没有就创建.