。在看过大量关于CGLIB的文章后,我总结了一下自己的理解。
首先先给出几个本文的几个对象
下面展示一些 代码片段
被代理对象
public class Student {
public void test1(int i){
System.out.println(i);
}
}
自己定义拦截的方法
public class ProxyForStudent implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before");
method.invoke(new Student(),1);
// methodProxy.invokeSuper(o,objects);
System.out.println("After");
return null;
}
}
测试类
public class test {
public static void main(String[] args) throws Exception {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "你想放的地方");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
enhancer.setCallback(new ProxyForStudent());
Student student = (Student) enhancer.create();
student.test1(1);
}
}
接下来展示关于上方生成的student的子类(也就是代理对象的源码)接下来我将这个类称为“小A”
/*只展示“小A”的重要的目标*/
public class Student$$EnhancerByCGLIB$$807c5493 extends Student implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$test1$0$Method;
private static final MethodProxy CGLIB$test1$0$Proxy;
final void CGLIB$test1$0(int var1) {
super.test1(var1);
}
public final void test1(int var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test1$0$Method, new Object[]{new Integer(var1)}, CGLIB$test1$0$Proxy);
} else {
super.test1(var1);
}
}
}
正如你所见到的,在测试类中使用的任何方法都是“小A”中的方法。接下来我来解释下所谓的重要的部分。
下方的“小A”的成员变量就是上方代码片段中的的 ProxyForStudent
private MethodInterceptor CGLIB$CALLBACK_0;
下方的Method对象就是 Student对象(被代理对象)中的Method对象,即是test1()
private static final Method CGLIB$test1$0$Method;
下方的MethodProxy 对象 是CGLIB 生成的 方法代理 (重点!!!)
private static final MethodProxy CGLIB$test1$0$Proxy;
介绍完所谓的几个重要部分后。
我们接下来讲一下执行流程和一些容易错误的点(关于为什么invoke会死循环)
首先,CGLIB会生成3个class文件
代理对象(小A) : Student$$EnhancerByCGLIB$$807c5493
代理对象的FastClass: Student$$EnhancerByCGLIB$$807c5493$$FastClassByCGLIB$$22cae79c
被代理对象的FastClass: Student$$FastClassByCGLIB$$22cae79c
Q:什么是FastClass,为什么可以加快执行效率
FastClass是CGLIB生成一个类,该类中通过swtich和对应传进来值执行对应的方法。
为什么这样做呢,为了解决反射的性能问题
是不是感觉解释的很模糊。接下来会讲解怎么执行的。
简单解释完FastClas后就是关于执行流程了
1.执行小A的test1
public final void test1(int var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test1$0$Method, new Object[]{new Integer(var1)}, CGLIB$test1$0$Proxy);
} else {
super.test1(var1);
}
}
如果写了代理方法,则进入代理方法(不考虑没写的情况)。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before");
method.invoke(new Student(),1);
// methodProxy.invokeSuper(o,objects);
System.out.println("After");
return null;
}
进入代理方法后会有一行 MethodProxy 的 invokeSuper(这里其实可以用invoke代替,但是参数不一样,不然会出现死循环)
invokeSuper()代码展示:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
首先会执行init() 。简单说下init的作用:init()是给MethodProxy所代理的方法 和 fastclass进行初始化
init()
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
createInfo记录了 c1 c2这两个fastclass,c1就是小A的fastclass ,c2就是被代理类的fastclass
FastClassInfo中记录4个重要的变量,f1,i1, f2,i2。f1是依据 c1生成的,f2同理
上文不是说FastClass可以加快这个方法执行速度嘛,这里是用swtich实现的。i1,i2就是各自fastclass中swtich 中case的数据。由于fastclass是已经生成好的,所以这里的init只是进行了的赋值操作。
初始化结束后进入 fastclass的invoke方法
fci.i2是17, obj 是 小A, args 是参数
fci.f2.invoke(fci.i2, obj, args);
此时var1 是 17
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
e58486bf var10000 = (e58486bf)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.clone();
case 4:
return var10000.newInstance((Callback[])var3[0]);
case 5:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 6:
return var10000.newInstance((Callback)var3[0]);
case 7:
var10000.test1(((Number)var3[0]).intValue());
return null;
case 8:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 9:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 10:
e58486bf.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 11:
e58486bf.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
return var10000.getCallback(((Number)var3[0]).intValue());
case 13:
return var10000.getCallbacks();
case 14:
return var10000.CGLIB$toString$2();
case 15:
return var10000.CGLIB$clone$4();
case 16:
return new Boolean(var10000.CGLIB$equals$1(var3[0]));
case 17:
var10000.CGLIB$test1$0(((Number)var3[0]).intValue());
return null;
case 18:
return new Integer(var10000.CGLIB$hashCode$3());
case 19:
return e58486bf.CGLIB$findMethodProxy((Signature)var3[0]);
case 20:
e58486bf.CGLIB$STATICHOOK1();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
所以整个流程是 执行小A的test1()方法,然后再通过写的代理方法 进入 methoproxy的invokesuper方法中。然后根据生成的数据(数据是 17)找到代理应该执行哪个方法然后快速执行 小A的方法。(有一种空间换时间的想法)
case 17:
var10000.CGLIB$test1$0(((Number)var3[0]).intValue());
return null;
回去看看小A中的方法
final void CGLIB$test1$0(int var1) {
super.test1(var1);
}
这是因为这种看似多此一举的行为,使得如果将上方的invokesuper如果改成 invoke会死循环。
invoke方法:
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
fci.f1.invoke(fci.i1, obj, args)是下方的代码
其中传入的fci.i1是0,obj 是代理对象,args 是参数
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
Student var10000 = (Student)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.test1(((Number)var3[0]).intValue());
return null;
case 1:
return new Boolean(var10000.equals(var3[0]));
case 2:
return var10000.toString();
case 3:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
因为上方传入的var2是小A。当invoke拿到小a,fastclass执行 小a中的 test1()。
然后test1()再次进入ProxyForStudent 中再次执行 invoke(),再次拿到小a,再次执行test1()就死循环了…
回顾下小A中test1的代码:this就是小a
public final void test1(int var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test1$0$Method, new Object[]{new Integer(var1)}, CGLIB$test1$0$Proxy);
} else {
super.test1(var1);
}
}
上方写的有点乱。
Fastclass 是为了 通过 键值快速执行传进来对象的方法的一个类
Methodproxy 中包含
被代理方法的信息,被代理方法在fastclass中的键值,和fastclass
通过被代理方法信息得出键值找出对应的case的方法然后快速执行
invokesuper 是使用被代理类fastclass,所以应该传入代理类
invoke 是使用被代理类的fastclass,所以应该传入被代理类
代理类是则是调用后,根据methodproxy快速执行自己的方法(疑惑行为)