CGLB动态代理
CGLB动态代理
一、CGLIB实现接口
public interface ProductInterface {
void test();
}
/**
* 用来测试接口
*/
private static void testInterface() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductInterface.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行切面逻辑");
return null;
}
});
ProductInterface productInterfaceProxy = (ProductInterface)enhancer.create();
productInterfaceProxy.test();
}
二、CGLIB实现类
对应的类:
public class UserService {
public void test(){
System.out.println("execute userService.......");
}
}
/**
* 用来测试类
*/
private static void testClass() {
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 当前的代理对象
* @param method 当前代理对象正在调用的方法
* @param objects 当前代理对象正在调用方法时候的参数
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始来执行切面逻辑");
return method.invoke(userService,objects);
}
});
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.test();
}
三、原理探究
3.1、找到代理类源文件
现在首要的是先找到CGLIB产生代理对象之后的文件中对应的源代码
首先在idea -> run -> edit configurations 中设置 vm options中,添加
-Dcglib.debugLocation=D:\project\study\spring\spring-xml\target
然后执行如下代码:
在对应的路径下找到对应的文件
3.2、找到代理类中的属性和方法
3.2.1、调用测试方法
因为对于接口或者是类来说,里面都有个test方法。
对于第一个方法来说,是不需要经过动态代理直接调用父类中的方法的;
对于第二个方法来说,这里会来执行方法拦截器中的拦截逻辑,来执行真正的代理逻辑方法。
首先分析,CGLIB$CALLBACK_0最开始的时候是没有进行赋值的,所以会从当前类中的CGLIB$BIND_CALLBACKS方法中查询,注意当前的this代表的是代理对象
if (var10000 == null) {
// 设置代理对象
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
3.2.2、方法拦截器在代理对象中如何进行设置的?
现在问题转换成了怎么从ThreadLocal中获取得到方法拦截器给当前的代理对象的。
所以就得先看CGLIB$THREAD_CALLBACKS是从哪里设置对应值的
那么只需要找一下这个方法在哪里使用的即可
发现这个方法是在Factory接口中的newInstance方法
那么也就是说,利用Factory接口中的newInstance方法是可以创建出来一个代理对象的
Factory factory = (Factory) enhancer.create();
Object newInstance = factory.newInstance(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return null;
}
});
手动利用Factory手动创建一个新的代理对象出来,但是一般不会这样子来进行操作。
所以方法拦截器到底是在哪里设置进去的呢?从代理对象执行方法会进入到方法拦截器中去,可以看到
UserService userServiceProxy = (UserService) enhancer.create();
应该是在上面这行代码中进行设置的
查看一下方法注释:先创建新的类,然后根据类去创建代理对象的过程
首先创建key,可以看到key中保存的有父类和接口信息,这么设计的目的是干嘛的?
因为最终这里会将key保存到一个map中去,而对应的value是对应的代理类。
因为不是每次调用下面这行代码的时候,都会去创建新的代理类
UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();
UserService userServiceProxy = (UserService) enhancer.create();
我们的目的是根据代理类创建新的代理对象,所以我们可以设置
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(true);
代理类可以重复获取得到,而不需要去重复的生成对应的代理类。
所以这里的生成的key就是为了来做一个缓存而已,表示是否复用第一次产生的代理类
所以在这行代码中既会产生代理类也会产生代理对象
Object result = super.create(key);
3.2.3、设置方法拦截器到线程上下文
利用代理类去创建代理对象!
那么看一下具体的实现逻辑
所以方法拦截器是在代理类生成之后,代理类对象生成之前,设置到代理类中去的。
所以产生代理对象之后,在调用方法的时候,就会去调用方法拦截器中的逻辑
四、MethodProxy
到现在还有一个参数没有来进行说明:MethodProxy
那么下面来说明一下这个类
最终生成的代理类中因为是继承了UserService,所以会有UserService中的方法
一个是自己代理方法增强后的逻辑,另外一个是执行父类中的方法
所以对于
// methodProxy代理的是test||CGLIB$test$0方法
// 如果方法名称是invoke的话,那么调用的是test方法
methodProxy.invoke(userService,objects);
// 如果方法名称是invokeSuper的话,那么调用的是CGLIB$test$0方法
methodProxy.invokeSuper(userService,objects);
但是需要注意的是,对于第一行代码来说,访问的是test方法,userService是有test方法的;
对于第二行代码来说,访问的是userService中的CGLIB$test$0方法,但是userService是没有CGLIB$test$0方法的
但是一定要注意的是下面这行代码:
methodProxy.invoke(o,objects);
这行代码一旦执行会发生栈溢出,因为执行代理对象的test方法,会不断的进入到方法拦截器中来。
那么methodProxy执行底层对应的逻辑是怎么样的?
后续会来进行补充说明。