JDK的动态代理需要被代理类要实现接口,但是Cglib不需要,Cglib会生成代理类的子类,并在代理类中对代理方法进行增强,CGlib的底层是采用的ASM字节码技术。
一切的学习从例子开始:
一、测试用例
1. 需要被代理的目标类
public class PersonC {
public void doSomething(){
System.out.println("doing");
}
public void doOtherthing(){
System.out.println("doing other");
}
}
2.增强方法
public class CglibProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
methodProxy.invokeSuper(o, objects);
System.out.println("end...");
return null;
}
}
3.测试类
public class TestCg {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
CglibProxy cglibProxy = new CglibProxy();
Enhancer e = new Enhancer();
e.setSuperclass(PersonC.class);
e.setCallback(cglibProxy);
PersonC target = (PersonC) e.create();
target.doSomething();
target.doOtherthing();
}
}
4.输出
before...
doing
end...
before...
doing other
end...
二、代理对象的生成:
2.1 代理对象是通过Enhancer类创建的。它是Cglib的字节码增强器。
2.2 创建关键步骤:
2.2.1 生成代理类的二进制字节码。
2.2.2 加载字节码,生成Class对象。
2.2.3 通过反射创建代理类对象。
2.3 抢先欣赏一下生成的代理类,便于后续理解。截取的是我本地能展示出来的Java代码(注意不是使用反编译工具打开的,使用IDE打开的,好像反编译工具有些代码编译不了)。
public class PersonC$$EnhancerByCGLIB$$2b07307e extends PersonC implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$doSomething$0$Method;
private static final MethodProxy CGLIB$doSomething$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$doOtherthing$1$Method;
private static final MethodProxy CGLIB$doOtherthing$1$Proxy;
private static final Method CGLIB$finalize$2$Method;
private static final MethodProxy CGLIB$finalize$2$Proxy;
private static final Method CGLIB$equals$3$Method;
private static final MethodProxy CGLIB$equals$3$Proxy;
private static final Method CGLIB$toString$4$Method;
private static final MethodProxy CGLIB$toString$4$Proxy;
private static final Method CGLIB$hashCode$5$Method;
private static final MethodProxy CGLIB$hashCode$5$Proxy;
private static final Method CGLIB$clone$6$Method;
private static final MethodProxy CGLIB$clone$6$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("PersonC$$EnhancerByCGLIB$$2b07307e");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$2$Method = var10000[0];
CGLIB$finalize$2$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$2");
CGLIB$equals$3$Method = var10000[1];
CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
CGLIB$toString$4$Method = var10000[2];
CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
CGLIB$hashCode$5$Method = var10000[3];
CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
CGLIB$clone$6$Method = var10000[4];
CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
var10000 = ReflectUtils.findMethods(new String[]{"doSomething", "()V", "doOtherthing", "()V"}, (var1 = Class.forName("PersonC")).getDeclaredMethods());
CGLIB$doSomething$0$Method = var10000[0];
CGLIB$doSomething$0$Proxy = MethodProxy.create(var1, var0, "()V", "doSomething", "CGLIB$doSomething$0");
CGLIB$doOtherthing$1$Method = var10000[1];
CGLIB$doOtherthing$1$Proxy = MethodProxy.create(var1, var0, "()V", "doOtherthing", "CGLIB$doOtherthing$1");
}
final void CGLIB$doSomething$0() {
super.doSomething();
}
public final void doSomething() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy);
} else {
super.doSomething();
}
}
final void CGLIB$doOtherthing$1() {
super.doOtherthing();
}
public final void doOtherthing() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$doOtherthing$1$Method, CGLIB$emptyArgs, CGLIB$doOtherthing$1$Proxy);
} else {
super.doOtherthing();
}
}
final void CGLIB$finalize$2() throws Throwable {
super.finalize();
}
protected final void finalize() throws Throwable {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$finalize$2$Method, CGLIB$emptyArgs, CGLIB$finalize$2$Proxy);
} else {
super.finalize();
}
}
final boolean CGLIB$equals$3(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
return var2 == null ? false : ((Boolean)var2).booleanValue();
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$4() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString();
}
final int CGLIB$hashCode$5() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$6() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$2$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$6$Proxy;
}
break;
case 1294226444:
if (var10000.equals("doOtherthing()V")) {
return CGLIB$doOtherthing$1$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$3$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$4$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$5$Proxy;
}
break;
case 2121560294:
if (var10000.equals("doSomething()V")) {
return CGLIB$doSomething$0$Proxy;
}
}
return null;
}
public PersonC$$EnhancerByCGLIB$$2b07307e() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
PersonC$$EnhancerByCGLIB$$2b07307e var1 = (PersonC$$EnhancerByCGLIB$$2b07307e)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
PersonC$$EnhancerByCGLIB$$2b07307e var10000 = new PersonC$$EnhancerByCGLIB$$2b07307e();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
PersonC$$EnhancerByCGLIB$$2b07307e var10000 = new PersonC$$EnhancerByCGLIB$$2b07307e();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
PersonC$$EnhancerByCGLIB$$2b07307e var10000 = new PersonC$$EnhancerByCGLIB$$2b07307e;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
根据代理类的源码,可以总结几点心得:
1.生成的代理类PersonC$$EnhancerByCGLIB$$2b07307e继承了PersonC(目标类),注意的是如果是被final的修饰的类或者方法是不能被代理的。
2.代理类会生成2个方法,一个是重写的doSomething方法,另一个是CGLIB$doSomething$0方法,在这个方法里其实也是直接调用了父类的方法。
3.当我们调用代理类的方法target.doSomething()时,首先会判断下是否实现了MethodInterceptor接口,如果实现的话就调用拦截器内的增强方法。
二、代理对象的调用过程分析:
public final void doSomething() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//如果有实现MethodInterceptor,就调用MethodInterceptor中的拦截器方法
if (var10000 != null) {
//参数列表:
//this: 当前代理对象
//CGLIB$doSomething$0$Method 需要执行的方法
//CGLIB$emptyArgs : 执行方法的参数
//CGLIB$doSomething$0$Proxy 代理方法的MethodProxy对象
var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy);
} else {
super.doSomething();
}
}
1. intercept方法内执行的就是我们自定义的拦截器方法,具体执行过程如下:内部调用的是invokeSuper方法
retValFromSuper = proxy.invokeSuper(obj, args);
2.具体的MethodProxy方法详情如下:但是看到这里,莫名的有点懵逼了,JDK动态代理内部最终的调用是使用的反射的方式来的,这个地方是怎么调用的?fastClass是什么东西?大致看样子是调用的fastClass内的某一个方法。
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();
}
}
3.稍微看下fastClass是什么玩意,通过debug了一下,我感觉它做的一件事就是映射(可能说的不是很准确)。
3.1 先来看看它的初始化操作:
private void init() {
if (this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
//CreateInfo对象内存储了目标类和代理类两个Class对象
MethodProxy.CreateInfo ci = this.createInfo;
//代构建fci实例
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//获取目标类Class
fci.f1 = helper(ci, ci.c1);
//获取代理类Class
fci.f2 = helper(ci, ci.c2);
//根据方法(doSomething)的签名sig1获取方法在目标类中的地址
fci.i1 = fci.f1.getIndex(this.sig1);
//根据方法(doSomething)的签名sig2获取方法在代理类中的地址
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci; //构建fastClass
this.createInfo = null;
}
}
}
}
3.2初始化完成之后的调用:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//获取到代理对象f2,执行对应方法(CGLIB$doSomething$0()V),最终其实是直接调用了父类方法。
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
通过跟踪源码,我们发现Cglib在执行具体方法时,
调用目标类的方法时不是使用的反射,而是直接调用,怪不得别人说Cglib的效率要比JDK动态代理要高呢。
最后的比较: