和JDK
代理一样,CGLib
代理也是一种动态代理方式,而且相比JDK
代理更加的灵活,可以代理任何类(除了final
修饰的)。
先上代码,还是以买房为例,看下具体实现:
需要被代理的类,这次不是某个接口的实现类,而是一个普通类
public class HouseBuyer {
public void buy() {
System.out.println("要买房");
}
}
代理类
public class CglibHouseCompanyProxy implements MethodInterceptor {
public Object getInstance(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("before method ...");
}
private void after() {
System.out.println("after method ...");
}
}
在被客户端实际调用的时候
HouseBuyer buyer = (HouseBuyer) new CglibHouseCompanyProxy().getInstance(new HouseBuyer());
buyer.buy();
是不是似曾相识,代理类都是实现了某个接口,然后重写一个方法,看着都是利用反射去获取被代理类的方法,经过增强处理然后去执行。
同样的,经过断点调试后会发现,真正执行方法的对象也是也是新的对象,
可以通过org.springframework.cglib.core
包里的DebuggingClassWriter
类将代理过程的字节码文件生成到磁盘上来简单分析下,在上面调用方法的前面加上
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "external-file/cglib-debug");
重新跑一遍代码,在项目根目录下会生成external-file
文件夹,在里面找到HouseBuyer$$EnhancerByCGLIB$$a1e313be
类进行反编译,节选部分代码
public class HouseBuyer$$EnhancerByCGLIB$$a1e313be extends HouseBuyer implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$buy$0$Method;
private static final MethodProxy CGLIB$buy$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("xxx.xxx.xxx.cglibproxy.HouseBuyer$$EnhancerByCGLIB$$a1e313be");
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$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$buy$0$Method = ReflectUtils.findMethods(new String[]{"buy", "()V"}, (var1 = Class.forName("xxx.xxx.xxx.cglibproxy.HouseBuyer")).getDeclaredMethods())[0];
CGLIB$buy$0$Proxy = MethodProxy.create(var1, var0, "()V", "buy", "CGLIB$buy$0");
}
final void CGLIB$buy$0() {
super.buy();
}
public final void buy() {
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$buy$0$Method, CGLIB$emptyArgs, CGLIB$buy$0$Proxy);
} else {
super.buy();
}
}
}
代理生成的类继承了被代理类,并且里面还有一个被final
修饰的buy
方法,这个方法里MethodInterceptor
获取引用之后就会执行intercept
方法,而我们的代理类也是MethodInterceptor
接口的实现类,所以在客户端类时间执行的时候最终执行的是代理类的intercept
方法。至于CGLib
是如何生成这个代理对象的,可以从代理类获取增强实例enhancer.create()
那里一步一步分析,这里就不做了。
小结
CGLib
通过为代理类生成一个继承的子类形式来代理,对被代理类没有限制,不需要像JDK
代理那样需要实现接口。底层通过字节码处理框架ASM
来生成Java
的字节码形成新的类,也就是上文新的代理类。简而言之:
-
JDK
代理是实现了被代理对象的接口,而CGLib
代理是继承了被代理对象 - 两者都是在运行期间生成字节码,
JDK
代理是直接写Class
字节码,而CGLib
是通过ASM
框架来写Class
字节码