两种动态代理
1、jdk的动态代理
讲一下动态代理的实现原理,说明白原理的话肯定是要看源码了,不要慌,干就完了!!!
其实在使用动态代理的时候最最核心的就是Proxy.newProxyInstance(loader, interfaces, h);废话不多说,直接干源码。
动态代理的样例代码:
Calculator.java
1 | package com.oi; |
MyCalculator.java
1 | package com.oi; |
CalculatorProxy.java
1 | package com.oi; |
Test.java
1 | package com.oi; |
动态代理的源码:
Proxy.java的newProxyInstance方法:
1 | public static Object newProxyInstance(ClassLoader loader, |
getProxyClass0(ClassLoader loader,Class<?>… interfaces)
1 | private static Class<?> getProxyClass0(ClassLoader loader, |
get()方法:
1 | public V get(K key, P parameter) { |
在此方法中会调用suppier.get方法,suppier就是Factory,此类定义在WeakCache内部
1 |
|
发现在此方法中调用了valueFactory.apply(key, parameter)方法,此对象其实就是Proxy中的ProxyClassFactory,会调用其apply方法
1 | // prefix for all proxy class names |
实际生成的方法是generateProxyClass
1 | public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { |
如果想要生成的话可以添加如下参数:
1 | System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); |
生成的代理类的反编译码为:
$Proxy.java
1 | // |
2、cglib的动态代理
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
操作代码:
MyCalculator.java
1 | package com.oi.cglib; |
MyCglib.java
1 | package com.oi.cglib; |
MyTest.java
1 | package com.oi.cglib; |
实现原理:
利用cglib实现动态代理的时候,必须要实现MethodInterceptor接口,此接口源码如下:
MethodInterceptor.java
1 | package net.sf.cglib.proxy; |
在我们的测试类中,大家发现了,我们创建了一个cglib的Enhancer对象,并且设置了父类、回调函数等参数,最重要的入口函数是create()方法:
1、Enhancer.create()
1 | /** |
2、Enhancer.createHelper()
1 | private Object createHelper() { |
preValidate()
1 | private void preValidate() { |
1 | private static final CallbackInfo[] CALLBACKS = { |
该方法主要是做验证并确定CallBack类型,我们使用的是MethodInterceptor,然后创建当前代理类的标识代理类,用这个标识代理类调用(AbstractClassGenerator)的create(key)方法创建,下面我们开始分析标识代理类创建逻辑 以及后面父类创建我们需要的代理类的逻辑。
标识代理类的创建类成员变量即KEY_FACTORY是创建代理类的核心
3、标识代理类
1、KEY_FACTORY
追踪源码可以看到,KEY_FACTORY在Enhancer的初始化即会创建一个final的静态变量
1 | private static final EnhancerKey KEY_FACTORY = |
2、Keyfactory_create方法
这儿可以看到使用key工厂创建出对应class的代理类,后面的KeyFactory_HASH_ASM_TYPE即代理类中创建HashCode方法的策略。我们接着点击源码查看
1 | public static KeyFactory create(ClassLoader loader, Class keyInterface, KeyFactoryCustomizer customizer, |
3、Generator的create方法
这儿创建了一个简易的代理类生成器(KeyFactory的内部类Generator ,与Enhancer一样继承自抽象类AbstractClassGenerator)来生成我们需要的标识代理类,我们接着看gen.create()方法
1 | public KeyFactory create() { |
4、AbstractClassGenerator的create(Key)方法
1 | protected Object create(Object key) { |
这个方法可以看到主要为根据类加载器定义一个缓存,里面装载了缓存的类信息,然后调用这个ClassLoaderData的get方法获取到数据,如果为class信息 那么直接使用反射实例化,如果返回的是实体类,则解析实体类的信息,调用其newInstance方法重新生成一个实例(cglib的代理类都会生成newInstance方法)
5、data.get(this,getUseCache)
1 | public Object get(AbstractClassGenerator gen, boolean useCache) { |
如果可以用缓存则调用缓存,不能调用缓存则直接生成, 这儿我们先看调用缓存的,在之前的步骤中,我们设置了一个key为ClassLoader,值为ClassLoaderData的缓存,这儿我们new了一个ClassLoaderData 并将类加载器传了进去 ,并且设置了这个Generator的key,我们看下new的逻辑
1 | public ClassLoaderData(ClassLoader classLoader) { |
可以看到每个类加载器都对应着一个代理类缓存对象 ,这里面定义了类加载器,缓存调用没查询到的调用函数,以及新建了一个LoadingCache来缓存这个类加载器对应的缓存,这儿传入的两个参数,load代表缓存查询失败时的回调函数,而GET_KEY则是回调时获取调用生成器的key 即4中传入的key 也即是我们的代理类标识符。 然后我们接着看generatedClasses.get(gen);的方法
6、generatedClasses.get(gen);
这个方法主要传入代理类生成器 并根据代理类生成器获取值返回。这儿主要涉及到的类就是LoadingCache,这个类可以看做是某个CLassLoader对应的所有代理类缓存库,是真正缓存东西的地方。我们分析下这个类
1 | package net.sf.cglib.core.internal; |
通过上面的分析可以得知,这个类主要作用是传入代理类生成器,根据这个代理类生成器以及代理类生成器的key来获取缓存,如果没有获取到则构建一个FutureTask来回调我们之前初始化时传入的 回调函数,并调用其中的apply方法,而具体调用的则是我们传入的代理类生成器的generate(LoadClassData)方法,将返回值覆盖之前的FutureTask成为真正的缓存。所以这个类的主要作用还是缓存。 这样则和5中不使用缓存时调用了一样的方法。所以我们接着来分析生成方法 generate(ClassLoadData),这儿因为我们使用的代理类生成器是Genrator,该类没有重写generate方法,所以回到了父类AbstractClassGenerator的generate方法
7、AbstractClassGenerator.generate 方法
1 | protected Class generate(ClassLoaderData data) { |
这个方法主要设置了下当前类生成器的类名,然后调用stratege的generate方法返回字节码,根据字节码 类名 类加载器将字节码所代表的类加载到内存中,这个功能看一下大概就懂,我们接下来主要分析字节码生成方法
8、DefaultGeneratorStrategy.generate(ClassGenerator cg)
1 | public byte[] generate(ClassGenerator cg) throws Exception { |
这里面主要是新建一个写入器,然后执行我们代理类生成器的generateClass方法将class信息写入这个ClassWriter 最后将里面的东西转换为byte数组返回,所以又回到了我们的代理类生成器的generateClass方法,这儿进入的是Generator的generateClass方法
9、Generator.generateClass(ClassVisitor v)
1 | //该方法为字节码写入方法 为最后一步 |
这个方法主要将一个完整的类信息写入ClassVisitor中,例如目前实现的Enhancer.EnhancerKey代理,即实现了newInstance方法, 重写了HashCode,toSting,equals方法,并将newInstance的所有参数作为了成员变量,这儿我们也可以看下具体实现newInstance方法的逻辑 即这个代码 EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance)); 如果有兴趣可以去研究asm字节码写入的操作
1 | public static void factory_method(ClassEmitter ce, Signature sig) |
可以得知 我们通过Generator创建的代理类大致内容应该如下,Enhancer.EhancerKey代理类字节码的class内容应该是把参数换为newInstance中的参数
1 | public class EnhancerKeyProxy extends xxx implements xxx{ |
最后执行传入的ClassVisitor 即我们传入的实例DebuggingClassWriter的toByteArray即可以将写入的内容转换为byte[]返回
至此 我们的成功的生成了Enhancer.EnhancerKey的代理类,也就是我们需要的代理类标识类 用来标识被代理的类,这个代理类主要用来作为被代理类的标识,在进行缓存时作为判断相等的依据。可以看到 cglib代理主要也是利用我们传入的被代理类信息来生成对应的代理类字节码,然后用类加载器加载到内存中。虽然我们的实际的代理任务才刚刚开始,但是要了解的东西已经基本上差不多了,对具体的我们案例中的ProxyFactory代理时,只是生成器Enhancer对比生成器Generator在生成过程中重写了一些操作而已。
4、回到步骤2,接着向下看执行的过程
1 | private Object createHelper() { |
可以看到获取到代理类标志类后 将其设置为当前代理类生成器的正在代理的类 并同样调用父类AbstractClassGenerator中create(key)的方法,下面开始分析Ehancer生成器的逻辑,由于部分逻辑和Generator生成器一致
5、AbstractClassGenerator.create方法
这个逻辑和上述步骤一致,查询当前key即代理类标志类对应的ClassLoadData缓存,如果没有则建一个空的缓存并初始化一个对应的ClassLoadData,传入相应的生成器,生成失败回调函数等
按照同样的逻辑一直走到generate(ClassLoadData)方法时,由于Enhancer生成器重写了这个方法 所以我们分析Enahncer的生成逻辑
6、Enhancer.generate(ClassLoadData data)
1 |
|
可以发现ehancer生成器只是做了个检查命名操作 在上面的Generator中也是做了个命名操作,然后继续执行父类的generate(data)方法,这个和上述步骤一致,我们主要看其中生成字节码的方法,即最后调用的Enhancer.generatorClass(ClassVisitor c)方法,
7、Enhancer.generatorClass(ClassVisitor c)
1 | public void generateClass(ClassVisitor v) throws Exception { |
可以看到这儿也是声明一个写入类 然后按照Ehancer的代理生成策略写入符合的class信息然后返回,最红依旧会执行toByteArray方法返回byte[]数组,这样则又回到了步骤中 根据类加载器 字节码数组来动态将代理类加载进内存中的方法了。最后我们回到根据class获取实例的代码即可返回被代理实例。 而我们执行方法时执行的是代理类中对应的方法,然后调用我们传入的callback执行 原理和jdk动态代理类似,至此 cglib动态代理源码分析到此结束。