cglib使用
导入cglib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
创建目标类
public class Target {
public void printName() {
System.out.println("nemo");
}
public void printAge() {
System.out.println(22);
}
}
创建拦截器
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before interceptor");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("after interceptor");
return obj;
}
}
测试
public static void main(String[] args) {
// 设置打印字节码文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Target.class);
enhancer.setCallback(new MyMethodInterceptor());
Target target = (Target) enhancer.create();
target.printName();
System.out.println("- - - - -");
target.printAge();
}
分析目标方法执行过程
打印字节码文件
设置属性打印字节码文件:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");
打开cglib目录,看到已经生成了字节码文件
代理类的printName()方法
在测试的main方法中,实际调用了代理类的printName方法
final void CGLIB$printName$0() {
// 执行父类(目标类)的方法
super.printName();
}
public final void printName() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
// 执行MethodInterceptor方法
var10000.intercept(this, CGLIB$printName$0$Method, CGLIB$emptyArgs, CGLIB$printName$0$Proxy);
} else {
super.printName();
}
}
从第15行代码看到,拦截器的4个参数分别是
- this 代理类对象
- CGLIB$printName$0$Method Method对象,目标类的printName方法
- CGLIB$emptyArgs 方法的参数
- CGLIB$printName$0$Proxy MethodProxy对象
以上三个参数在代理类的静态代码块中初始化
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("org.example.Target$$EnhancerByCGLIB$$1893d03d");
Class var1;
// 初始化Object对应的参数
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");
// 初始化目标类对应的参数
var10000 = ReflectUtils.findMethods(new String[]{"printName", "()V", "printAge", "()V"}, (var1 = Class.forName("org.example.Target")).getDeclaredMethods());
CGLIB$printName$0$Method = var10000[0];
CGLIB$printName$0$Proxy = MethodProxy.create(var1, var0, "()V", "printName", "CGLIB$printName$0");
CGLIB$printAge$1$Method = var10000[1];
CGLIB$printAge$1$Proxy = MethodProxy.create(var1, var0, "()V", "printAge", "CGLIB$printAge$1");
}
MethodProxy的create方法和init方法
静态代码块中,初始化MethodProxy,调用了create方法:
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 MethodProxy.CreateInfo(c1, c2);
return proxy;
}
- c1 目标类型org.example.Target
- c2 代理类型org.example.Target$$EnhancerByCGLIB$$1893d03d
- desc 方法参数类型和返回值类型()V
- name1 目标方法名printName
- name2 代理方法名CGLIB$printName$0
- sig1 目标方法签名
- sig2 目标方法签名
- createInfo.c1 目标类型org.example.Target
- createInfo.c2 代理类型org.example.Target$$EnhancerByCGLIB$$1893d03d
MethodProxy类的invokeSuper方法
在拦截器类中,调用了MethodProxy的invokeSuper方法:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
- obj 代理类对象
- args 方法入参
首先调用init方法初始化FastClass:
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.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;
}
}
}
}
this.createInfo已经在上面的create方法中初始化。
new MethodProxy.FastClassInfo()简单的构造了fci对象,并未初始化其他资源。
fci.f1是目标类的FastClass对象,类型为Target$$FastClassByCGLIB$$c6b7aaff,是上图打印的字节码文件。
fci.f2是代理类的FastClass对象,类型为Target$$EnhancerByCGLIB$$1893d03d$$FastClassByCGLIB$$73d76a45,也是上图打印的字节码文件。
this.sig1、this.sig2上面已经介绍过,在MethodProxy#create方法中初始化。
fci.i2是22(查看字节码文件的getIndex,在第17行代码赋值)
Target$$EnhancerByCGLIB$$1893d03d$$FastClassByCGLIB$$73d76a45#getIndex
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
// 忽略其他代码...
case 1748634388:
if (var10000.equals("CGLIB$printName$0()V")) {
return 22;
}
break;
// 忽略其他代码...
case 2011844968:
if (var10000.equals("CGLIB$clone$5()Ljava/lang/Object;")) {
return 10;
}
}
return -1;
}
再回到invokeSuper方法,fci在init方法中做了初始化。
继续看fci.f2的invoke方法
代理类的FastClass对象的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
1893d03d var10000 = (1893d03d)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
// 忽略其他代码...
case 22:
var10000.CGLIB$printName$0();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
var1的值是22,在MethodProxy#invokeSuper方法中赋值。
var2是代理类对象。
var3是方法入参。
在invoke方法的11行,执行了代理类的CGLIB$printName$0方法。最后执行了父类(目标类的)printName方法。
final void CGLIB$printName$0() {
super.printName();
}
执行流程图
这里为cglib调用流程做一下总结,从main方法开始,中间执行了MethodInterceptor#intercept方法,最后执行了目标类Target:printName方法。
源代码地址
大家有兴趣可以从gitee上下载我的demo:cglib练习代码