文章目录
- 一、代理方法长什么样
- 代码示例:
- 二、代理方法逻辑
- 三、代理对象初始化过程
- 四、总结
一、代理方法长什么样
代码示例:
//被代理类
class UserService {//方法必须能被重写,不能被final修饰public void test() {System.out.println("调用test方法了...");}
}//拦截器(方法增强)
class MyApiInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("被代理方法执行前"); // 此处可以做一些操作Object result = proxy.invokeSuper(obj, args);//执行被代理方法System.out.println("被代理方法执行后" ); // 方法调用之后也可以进行一些操作return result;}
}//测试
public class TestCglib {public static void main(String[] args) {//生成一个增强器Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); //设置要代理的类enhancer.setCallback(new MyApiInterceptor()); //设置回调的拦截器// 创建代理对象Person person=enhancer.create();person.test();}
}
刚开始接触动态代理,设想的代理方法是如下的样子,是不是很简单粗暴,这么想是不是也挺合理。
其实代理方法长这样:
下面的test方法就是生成的代理方法,其中没有我们写的增强代码,也没有用super.test()去调用被代理方法,而是先获得一个拦截器,然后执行intercept()方法。
既然代理类是运行时自动生成的,为什么不直接把增强代码粘到代理方法中?
行,但是不优雅。
拦截器提供了一种可插拔的方式,使增强逻辑与目标方法解耦,能够灵活地对代理逻辑进行配置和管理。
看看拦截器是如何起作用的。
二、代理方法逻辑
从上面的例子看,我们调用代理对象的test方法,是如何去执行拦截器【MyApiInterceptor类】中的逻辑呢?
这要看代理类重写的test方法做了写什么:
从程序执行的结果上看,图上MethodInterceptor的实现类就是我们自定义的MyApiInterceptor,这里需要说明一下intercept方法的四个参数都是什么:
- 参数1:代理类对象
- 参数2:被代理类的方法对象(Method类型对象)
- 参数3:方法入参(Object[])
- 参数4:代理类的方法对象(MethodProxy类型对象)
代理方法如何得到我们自定义的MyApiInterceptor对象?
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
上面是test()方法的第一句代码,其中的(CGLIB$CALLBACK_0)属性是MethodInterceptor类型的,自定义的MyApiInterceptor正好实现了MethodInterceptor接口,这里明显是使用了里式替换。
看哪里给(CGLIB C A L L B A C K 0 )属性赋值了,可以找到构造函数中挑中了【 C G L I B CALLBACK_0)属性赋值了,可以找到构造函数中挑中了【CGLIB CALLBACK0)属性赋值了,可以找到构造函数中挑中了【CGLIBBIND_CALLBACKS方法】,内容如下:
//代理类构造函数public UserService$$EnhancerByCGLIB$$9391ced0() {CGLIB$BIND_CALLBACKS(this);}
现在我们需要知道什么时候在缓存(CGLIB$THREAD_CALLBACKS属性)中存了自定义的MyApiInterceptor对象。
在代理类中只找到一个静态方法【CGLIB$SET_THREAD_CALLBACKS】给上述属性赋值了,没有在本类中看到谁调用了这个方法。
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {CGLIB$THREAD_CALLBACKS.set(var0);}
所以我们要知道,是谁在什么地方调用了静态方法【CGLIB$SET_THREAD_CALLBACKS】
三、代理对象初始化过程
从demo中看出,创建代理对象是通过enhancer.create();创建的,需要从【Enhancer 类】的【create方法】开始抽丝剥茧找到真相。看个源码和破案一样。
//生成一个增强器Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); //设置要代理的类enhancer.setCallback(new MyApiInterceptor()); //设置回调的拦截器// 创建代理对象Person person=enhancer.create();
方法【createUsingReflection】的第一句代码调用了方法【setThreadCallbacks】,并把代理类的Class对象传给了它。这个方法名的意思就是给ThreadCallbacks赋值,下图看该方法逻辑
这里确定方法名SET_THREAD_CALLBACKS_NAME就是代理类中的静态方法【CGLIB$SET_THREAD_CALLBACKS】。另外因为是静态方法,反射调用时不用传递代理类对象,传个null就可以。
标题2中我们的问题我们在这里就可以揭晓。
是谁在什么时候调用了静态方法【CGLIB S E T T H R E A D C A L L B A C K S 】,给( C G L I B SET_THREAD_CALLBACKS】,给(CGLIB SETTHREADCALLBACKS】,给(CGLIBTHREAD_CALLBACKS属性)中存了自定义的MyApiInterceptor对象?
who:Enhancer对象
when:在创建了代理类之后,马上要生成代理类对象之前
四、总结
CGLIB拦截器的作用主要有以下几个方面:
-
增加额外的逻辑:
拦截器可以在目标方法执行前后,或者在方法执行过程中插入额外的逻辑。例如,可以在方法调用前进行权限检查、日志记录等操作,或者在方法调用后进行结果处理、异常处理等操作。通过拦截器,可以在不修改目标类的情况下,增加额外的功能。 -
实现横切关注点的分离:
拦截器可以将与核心业务逻辑无关的横切关注点(cross-cuttingconcern)与核心业务逻辑分离。横切关注点包括日志记录、性能统计、事务管理等,这些功能可以通过拦截器统一管理,而不需要在每个目标方法中重复编写代码。 -
实现代理的灵活配置和管理:
拦截器提供了一种可插拔的方式,使得增强逻辑可以与目标方法解耦,方便对代理逻辑进行配置和管理。通过拦截器,可以动态地添加、删除或修改代理逻辑,而无需修改目标类的源代码。
CGLIB拦截器的作用是在代理类中插入增强逻辑,实现与目标方法的拦截、预处理和后处理操作,同时实现了横切关注点的分离和代理的灵活配置。它是CGLIB实现动态代理的重要组成部分,提供了一种强大的方式来扩展和定制代理类的行为。