动态代理作为Spring中重要的一个技术,实现了AOP的动态代理,本文基于java的CGLIB动态代理对该技术的实现进行深入的了解。
测试代码如下所示:
// 客户端代码
public class TestCglib {
public static void main(String[] args) {
//该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\cglibClass");
Enhancer enhancer = new Enhancer();
//继承被代理类
enhancer.setSuperclass(URLController.class);
//设置回调
enhancer.setCallback(new URLRequestMethodInterceptor());
//生成代理类对象
URLController urlController = (URLController)enhancer.create();
//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
urlController.urlRequest1();
urlController.urlRequest2();
}
}
// 目标类
public class URLController {
public void urlRequest1() {
System.out.println("now request url1 ...");
try {
Thread.sleep(1000);
}catch (Exception e){
}
}
public void urlRequest2() {
System.out.println("now request url2 ...");
try {
Thread.sleep(2000);
}catch (Exception e){
}
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
// 拦截器类,用于被代理类回调
public class URLRequestMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] arg,
MethodProxy proxy) throws Throwable {
System.out.println("before:"+method.getName());
Object object = proxy.invokeSuper(obj, arg);
System.out.println("after:"+method.getName());
return object;
}
}
复制代码
- 创建Enhancer对象
图 1-1 客户端创建Enhancer对象
图 1-2 执行EnhanceKeyr的静态变量初始化
图 1-3 执行KeyFactory的第一个create方法
图 1-4 执行KeyFactory的第二个create方法
图1-5 执行KeyFactory的第三个create方法
第三个create方法里面,会自己创建Generator对象,用于生成Enhancer字节码文件。
图1-6 执行 Generator的create方法来创建Enhancer类
keyInterface.getName()的值为net.sf.cglib.proxy.Enhancer$EnhancerKey
图1-7 生成代理类字节码文件
- getClassName方法是命名规则函数,Enhancer$EnhancerKey$$KeyFactory
ByCGLIB$$7fb24d72的名称则是这个方法生成的。
-
cache2是一个hashMap结构,有两个元素。第一个元素的key为String类型的“net.sf.cglib.proxy.EnhancerEnhancerKey“的Map,则创建Class类型的net.sf.cglib.proxy.Enhancer$EnhancerKey的引用, 该引用是WeakReference类型。
private Set getClassNameCache(ClassLoader loader) {
// NAME_KEY = new Object();初始化
return (Set)((Map)source.cache.get(loader)).get(NAME_KEY);
} -
ReflectUtils生成代理类在虚拟机中,即Class对象(net.sf.cglib.proxy.EnhancerEnhancerKey7fb24d72,生成该字节码文件并加入到虚拟机中。
图1-7 创建Enhancer对象
此时生成Enhancer$EnhancerKey7fb24d72对象,路径是\net\sf\cglib\proxy,变量KeyFactory初始化结束,也就是Emhancer对象创建完成。
2 生成代理类字节码文件
图1-8 客户端创建目标类的代理类
图1-8 执行Enhancer对象创建代理对象的过程
此时,代理类字节码文件已经生成,并且已经加载到java虚拟机里面。
(3)代理类创建对象(生成MethodProxy类和MethodProxy对象)
和之前创建Enhancer对象一样,生成目标类的代理类对象,生成对象之后会进行一些初始化操作,打开class源码文件,如下所示:
图1-9 代理类的字节码文件
从上面的初始化代码可以看出,用目标类对象来初始化Method对象,用代理类的对象来初始化MethodProxy对象,目标类的每个方法对应一个Method对象,代理类的每个方法对应一个MethodPoxy对象。
图 1-10 目标类方法对应的代理类方法
从代理类的方法可以知道,调用方法最终会调用MethodInterceptor的intercept(Object obj, Method method, Object[] arg,MethodProxy proxy)方法。该方法会调用methodProxy.invokerSuper方法,源码如下:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
// 处理FastClass相关的配置
init();
// 获取FastClassInfo对象fci
FastClassInfo fci = fastClassInfo;
// fci方法调用,f2是代理类的方法,i2是代理类方法索引
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
复制代码
从上面的methodProxy的源码可以看出,首先给代理类创建FastClass类(减少反射时候的耗时问题),用于加快方法调用的时间,每次调用时如果存在FastClass时,直接根据索引调用相应的方法会比反射机制快不少。调用代理类的CGLIB0方法,该方法调用super.urlRequest1()方法,最终成功调用目标类的方法(代理类的父类);注意:FastClass类只有invoke方法,代理类没有。MethodProxy类有invoke和invokeSuper方法。
如果MethodInterceptor的intercept(Object obj, Method method, Object[] arg,MethodProxy proxy)方法调用methodProxy.invoker方法,则会产生循环调用的问题。源码如下所示:
methodProxy.invoker方法会调用FastClass目标类的方法(fci.f1.invoke(fci.i1,obj,args)),FastClass目标类的invoke方法会先把代理类强制转换成目标类,然后使用switch语句获取调用的方法var10000.urlRequest1(),最终调用的还是目标类的方法,又回调用拦截器,造成死循环。目标类的FastClass的invoke方法如下所示:
代理类的fastClass的invoke方法如下所示: