- JDK 动态代理实现与原理
首先来看一段CGLib代理的测试代码(MethodInterceptor的测试, 其他类型这里不做展开了). Util类的代码在后面给出的码云片段中
public
下面的输出结果除了测试动态代理生效结果外, 还将动态代理生成的类名也输出出来了. 这些类名信息, 在后面的分析中会用到.
Current Pid is:54801
co/wangming/cglib/methodinterceptor/MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
proxy start : class net.sf.cglib.proxy.MethodProxy: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
*****************CreateInfo***********************
Proxy Class: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$printHi$0$Proxy CreateInfo : class net.sf.cglib.proxy.MethodProxy$CreateInfo
CGLIB$printHi$0$Proxy CreateInfo c1: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A
CGLIB$printHi$0$Proxy CreateInfo c2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$equals$1$Proxy CreateInfo : class net.sf.cglib.proxy.MethodProxy$CreateInfo
CGLIB$equals$1$Proxy CreateInfo c1: class java.lang.Object
CGLIB$equals$1$Proxy CreateInfo c2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$toString$2$Proxy CreateInfo : class net.sf.cglib.proxy.MethodProxy$CreateInfo
CGLIB$toString$2$Proxy CreateInfo c1: class java.lang.Object
CGLIB$toString$2$Proxy CreateInfo c2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$hashCode$3$Proxy CreateInfo : class net.sf.cglib.proxy.MethodProxy$CreateInfo
CGLIB$hashCode$3$Proxy CreateInfo c1: class java.lang.Object
CGLIB$hashCode$3$Proxy CreateInfo c2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$clone$4$Proxy CreateInfo : class net.sf.cglib.proxy.MethodProxy$CreateInfo
CGLIB$clone$4$Proxy CreateInfo c1: class java.lang.Object
CGLIB$clone$4$Proxy CreateInfo c2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
*****************CreateInfo***********************
hi
proxy over
hi
*****************FastClassInfo***********************
Proxy Class: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
CGLIB$printHi$0$Proxy FastClassInfo : class net.sf.cglib.proxy.MethodProxy$FastClassInfo
CGLIB$printHi$0$Proxy FastClass f1: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$FastClassByCGLIB$$65f2d708
CGLIB$printHi$0$Proxy FastClass f2: class co.wangming.cglib.methodinterceptor.MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc$$FastClassByCGLIB$$19e0f1ba
CGLIB$printHi$0$Proxy FastClass i1: 0
CGLIB$printHi$0$Proxy FastClass i2: 14
---->
---->
---->
---->
****************FastClassInfo************************
由于生成的代理类的代码过于长, 而知乎没有折叠功能, 所以我将这个代码片段放到了码云上面 . A的代理类的名称是: MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc
cglib 动态代理 - 代码片段 - 码云 Gitee.comgitee.com从printHi()方法入手, 看看它的代理是怎么实现的.
// 在MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc类内部, 有下面俩个和printHi方法相关的属性
上面简单分析了代理子类的实现, 但是这都不是重点, 真正的魔法在callback里面. demo里面callback是这么写的
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("proxy start : " + proxy.getClass() + ": " + obj.getClass());
Object res = proxy.invokeSuper(obj, args);
System.out.println("proxy over");
return res;
});
proxy参数的类型是MethodProxy类型, MethodProxy有俩个invoke方法:
- invoke(Object obj, Object[] args): obj参数不能是MethodInterceptor#intercept()方法的第一个参数obj对象, 否则会造成栈溢出
- invokeSuper(Object obj, Object[] args): obj参数必须是MethodInterceptor#intercept()方法的第一个参数obj对象
在使用MethodInterceptor的时候, 一定要注意上面这俩点, 下面我们就从invoke/invokeSuper方法入手, 分析一下.
由于这一块的源码过多, 我就不一一都贴出来了, 我画了俩张图帮大家理顺一下.
MethodProxy类里面有一个CreateInfo对象. CreateInfo内部有俩个Class对象, 分别是
- c1: 目标类的Class对象
- c2: 目标类的强化类的Class对象, 也就是CGLib生成的目标类的代理子类
CGLib会利用CreateInfo对象去构建出FastClassInfo这个对象. 构建过程就是通过CreateInfo的c1/c2去分别构建出FastClassInfo里面的FastClass类型的f1/f2.
也就是说, 真正的是构建了俩个FastClass对象出来. FastClass对象是通过FastClass内部类Generator进行构建的. 而内部类Generator是将构建过程交给了它的父类AbstractClassGenerator#generate()方法的.
AbstractClassGenerator#generate()又是通过其内部类GeneratorStrategy的对象执行了构建. 最终实现构建的是FastClass#Generator()的generateClass()方法, 该方法实例化了一个FastClassEmitter对象, FastClassEmitter对象内部就是通过ASM去构建Class对象的.
invoke/invokeSuper方法实现如下
public
init()方法如下
private
针对init()方法的过程可以参考一下下面的时序图
可以看出来, 整个CGLib的核心就在于这个FastClass
abstract
FastClass是一个抽象类, CGLib在运行时通过FastClass内的Generator这个内部类将其子类动态生成出来, 然后再利用ClassLoader将生成的子类加载进JVM里面去.
其实, CGLib会为我们生成很多个代理类, 不单单是目标类的子类, 例如上文提到的FastClass f1, FastClass f2的子类是不同的.
- MethodInterceptorTest$A$$FastClassByCGLIB$$65f2d708 : FastClassInfo#f1, MethodProxy的invoke()方法进行调用
- MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc$$FastClassByCGLIB$$19e0f1ba : FastClassInfo#f2, MethodProxy的invokeSuper()方法进行调用
public
在刚开始的demo中, 如果进行如下调用, 会发生递归.
eznhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("proxy start : " + proxy.getClass() + ": " + obj.getClass());
Util.printCreateInfo(list.get(0));
Object res2 = proxy.invoke(obj, args1); // 注意,这里将invokeSuper()换成了invoke()
System.out.println("proxy over");
return res2;
});
这是因为(FastClassInfo#i1 的值为0, 因此var1为0, 刚开始的运行日志有输出 )
public
此时的var10000就是obj, 那么流畅就成了又会去调用MethodInterceptorTest$A$$EnhancerByCGLIB$$b5ca7abc的printHi()
public
因此当调用MethodProxy的invoke()方法时, 必须不能是MethodInterceptor#intercept的第一个obj参数.
而invokeSuper就不会有这个问题, FastClassInfo#i2的值为 14,
public
因此执行的是var10000.CGLIB$printHi$0();这个方法
final void CGLIB$printHi$0() {
super.printHi();
}
这是直接调用A的printHi()方法进行调用.
好了, 到这里分析就告一段落, 下次会分析下, cglib是如何利用ASM生成代理类的.