前言
在Spring之AOP的最后,我们提到了两种动态代理的方式:JDK动态代理以及cglib动态代理;JDK方式可以代理接口或者实现了接口的实现类,cglib的被代理类不能是接口/抽象类。本文基于JDK1.8。
CGLIB
之前是在项目里引入spring全家桶来看源码的,所以一开始看Enhancer
是在org.springframework
下还以为是Spring自己重写的一套。通过jar包方式查看,发现很多相关的类都不能看到源码,就去github上拉了springframework
;在spring-core
这个模块下的spring-core.gradle
有这么一段:
task cglibRepackJar(type: ShadowJar) {
baseName = 'spring-cglib-repack'
version = cglibVersion
configurations = [project.configurations.cglib]
relocate 'net.sf.cglib', 'org.springframework.cglib'
relocate 'org.objectweb.asm', 'org.springframework.asm'
}
就是cglib用的还是net.sf.cglib
,于是直接下了net.sf.cglib
源码。附上地址:https://gitee.com/mirrors/cglib
源码
使用
我们先看一个例子,并想下打印出来是怎样的。
public class MyEnhancerTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyTest.class);
enhancer.setCallback(new MyInterceptor());
MyTest myTest = (MyTest) enhancer.create();
myTest.print("hello world");
}
static class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置操作");
Object ret = proxy.invokeSuper(obj, args);
System.out.println("后置操作");
return ret;
}
}
public void print(String input) {
System.out.println(input + ":" + this.getClass().getName());
}
}
现在我们看下打印的内容:
前置操作
hello world:MyEnhancerTest$$EnhancerByCGLIB$$49c4991a
后置操作
看打印的内容我们知道代理成功了,并且新的代理类名为MyEnhancerTest$$EnhancerByCGLIB$$49c4991a
。
输出代理类
在单元测试方法中加上
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
就可以在target/cglib
目录下输出cglib动态生成的类。
先了解下这3个类继承了什么父类,实现了什么接口。
public class MyEnhancerTest$$EnhancerByCGLIB$$49c4991a extends MyEnhancerTest implements Factory{}
public class MyEnhancerTest$$EnhancerByCGLIB$$49c4991a$$FastClassByCGLIB$$285c3a4a extends FastClass{}
public class MyEnhancerTest$$FastClassByCGLIB$$ef1ff622 extends FastClass{}
CGLIB是利用ASM框架来生成字节码文件的。ASM 是一个Java字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。
分析
在使用
中,我们看到打印出来的类名就是MyEnhancerTest$$EnhancerByCGLIB$$49c4991a
,我们就从这个类开始进行分析吧。
类的内容比较多,我们按需粘贴代码进行分析,最好是在自己机器上执行一遍,看下类的全貌。
一进来首先是一个静态代码块CGLIB$STATICHOOK1()
方法,CGLIB$STATICHOOK1()
主要是初始化一些类属性。
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
// 代理类
Class var0 = Class.forName("MyEnhancerTest$$EnhancerByCGLIB$$49c4991a");
// 父类,会根据方法而变动
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$2$Method = var10000[0];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[1];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[2];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[3];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
// 这里var1赋值成`MyEnhancerTest`
var10000 = ReflectUtils.findMethods(new String[]{"print", "(Ljava/lang/String;)V", "test", "()V"}, (var1 = Class.forName("MyEnhancerTest")).getDeclaredMethods());
CGLIB$print$0$Method = var10000[0];
// 根据父类/子类,以及两个类里的方法名生成`MethodProxy`, `MethodProxy`很重要,我们的`MethodInterceptor::intercept`就传入了这个参数
CGLIB$print$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "print", "CGLIB$print$0");
CGLIB$test$1$Method = var10000[1];
CGLIB$test$1$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$1");
}
MethodProxy.create
方法将父/子类,以及方法信息存到MethodProxy
中,后续会用到。
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
namingPolicy = fromEnhancer.getNamingPolicy();
strategy = fromEnhancer.getStrategy();
attemptLoad = fromEnhancer.getAttemptLoad();
}
}
当我们调用print
方法时,执行的是这么一段代码。
public final void print(String var1) {
// 我们之前在`Enhancer`里设置的`callback`, 这里就是`MyEnhancerTest.MyInterceptor`
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
// 看到这里执行里`MyInterceptor`的`intercept`方法, 传入了当前对象,`MyEnhancerTest.print`的方法反射对象, 入参,以及`CGLIB$print$0$Proxy`(`CGLIB$print$0$Proxy`上文提过,是一个`MethodProxy`里面存储了父/子类以及`print`