spring|别再说 Spring AOP 默认用的是 JDK 动态代理

spring|别再说 Spring AOP 默认用的是 JDK 动态代理

文章图片

spring|别再说 Spring AOP 默认用的是 JDK 动态代理

文章图片

spring|别再说 Spring AOP 默认用的是 JDK 动态代理

文章图片


话不多说 , 发车!
说下 AOPAOP , Aspect Oriented Programming , 面向切面编程 。
将一些通用的逻辑集中实现 , 然后通过 AOP 进行逻辑的切入 , 减少了零散的碎片化代码 , 提高了系统的可维护性 。
具体是含义可以理解为:通过代理的方式 , 在调用想要的对象方法时候 , 进行拦截处理 , 执行切入的逻辑 , 然后再调用真正的方法实现 。
例如 , 你实现了一个 A 对象 , 里面有  addUser 方法 , 此时你需要记录该方法的调用次数 。
那么你就可以搞个代理对象 , 这个代理对象也提供了 addUser 方法 , 最终你调用的是代理对象的 addUser, 在这个代理对象内部填充记录调用次数的逻辑 , 最终的效果就类似下面代码:
Enhancer en = new Enhancer();
//2.设置父类 , 也就是代理目标类 , 上面提到了它是通过生成子类的方式
en.setSuperclass(target.getClass());
//3.设置回调函数 , 这个this其实就是代理逻辑实现类 , 也就是切面 , 可以理解为JDK 动态代理的handler
en.setCallback(this);
//4.创建代理对象 , 也就是目标类的子类了 。
return en.create();

JDK 动态代理和 CGLIB 两者经常还可能被面试官问性能对比 , 所以咱们也列一下(以下内容取自:haiq的博客):

  • jdk6 下 , 在运行次数较少的情况下 , jdk动态代理与 cglib 差距不明显 , 甚至更快一些;而当调用次数增加之后 ,cglib 表现稍微更快一些
  • jdk7 下 , 情况发生了逆转!在运行次数较少(1000000)的情况下 , jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50000000) ,动态代理比 cglib 快了接近1倍
  • jdk8 表现和 jdk7 基本一致
我没试过 , 有兴趣的同学可以自己实验一下 。
能说说拦截链的实现吗?我们都知道 Spring AOP 提供了多种拦截点 , 便捷我们对 AOP 的使用 , 比如 @Before、@After、@AfterReturning、@AfterThrowing 等等 。
方便我们在目标方法执行前、后、抛错等地方进行一些逻辑的切入 。
那 Spring 具体是如何链起这些调用顺序的呢?
这就是拦截链干的事 , 实际上这些注解都对应着不同的 interceptor 实现 。
然后 Spring 会利用一个集合把所有类型的 interceptor 组合起来 , 我在代码里用了 @Before、@After、@AfterReturning、@AfterThrowing这几个注解 。
于是集合里就有了这些 interceptor(多了一个 expose...等下解释) , 这是由 Spring 扫描到注解自动加进来的:



然后通过一个对象 CglibMethodInvocation 将这个集合封装起来 , 紧接着调用这个对象的 proceed 方法 , 可看到这个集合 chain 被传入了 。



我们来看下 CglibMethodInvocation#proceed 方法逻辑 。
要注意 , 这里就开始 递归套娃 了 , 核心调用逻辑就在这里:



可以看到有个 currentInterceptorIndex 变量 ,  通过递归 , 每次新增这索引值  , 来逐得到下一个 interceptor。
并且每次都传入当前对象并调用  interceptor#invoke, 这样就实现了拦截链的调用 , 所以这是个递归 。