Spring 源码阅读 68:基于 JDK 的 AOP 代理拦截器链执行(2)- ReflectiveMethodInvocation 分析

简介: 【1月更文挑战第3天】本文分析了 ReflectiveMethodInvocation 类中的proceed方法,通过对这个方法的分析,了解了连接器链中的增强逻辑是如何逐层执行的,以及目标方法是什么时候被调用的。


基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

接上一篇:Spring 源码阅读 67:基于 JDK 的 AOP 代理拦截器链执行(1)- 准备工作

概述

上一篇分析了 JdkDynamicAopProxy 的invoke方法在获取到拦截器链之后,对于拦截器链为空的情况,会直接执行目标方法,对于拦截器链不为空的情况,会将代理对象、目标方法、拦截器链等信息,封装为一个 ReflectiveMethodInvocation 对象,然后通过它的proceed方法完成拦截器中的增强逻辑和目标方法的执行。

本文我们重点分析拦截器链执行的核心逻辑,也就是 ReflectiveMethodInvocation 中的proceed方法。

ReflectiveMethodInvocation 的核心逻辑

先看proceed方法的源码。

这个方法值得仔细分析,我们先看整体逻辑。

首先是一个if判断,this.currentInterceptorIndex是否等于拦截器链的长度减1,这个判断语句中,currentInterceptorIndex直接翻译过来就是当前拦截器索引。所以,这句代码是在判断当前当前是不是执行到了拦截器链的最后一个拦截器。如果是,则执行if语句块中的invokeJoinpoint方法,根据方法名字,这里应该是执行目标方法。所以,这里整体的逻辑就是,如果执行到了拦截器链的最后一个拦截器,那么则执行目标方法并返回结果,否则就执行后面的逻辑。

后面的逻辑就是拦截器链没有执行到最后一个拦截器,那么就接着执行拦截器。从拦截器链中,通过索引++this.currentInterceptorIndex来得到下一个拦截器。然后,通过之后的if判断,来确定拦截器执行的逻辑。

判断条件是当前获取到的拦截器,是否是 InterceptorAndDynamicMethodMatcher 类型,那拦截器什么情况下会是这个类型呢?其实 InterceptorAndDynamicMethodMatcher 这个类型我们之前见过。

回顾一下之前的内容。在获取拦截器链的过程中,会通过 Advisor 获取切入点的 MethodMatcher 来判断目标方法与增强逻辑是否匹配。如果匹配的话,还会判断 MethodMatcher 的isRuntime方法结果是否为true,如果是的话,则表示方法匹配的逻辑是动态的,在执行增强逻辑之前在执行匹配,这种情况下,最终得到的拦截器会是一个封装了拦截器本身和 MethodMatcher 的拦截器,它的类型就是 InterceptorAndDynamicMethodMatcher。具体的分析可以参考之前的文章。

传送门:Spring 源码阅读 64:基于 JDK 的 AOP 代理如何获取拦截器链(2)- Advisor 与目标方法的匹配

如果当前的拦截器是 InterceptorAndDynamicMethodMatcher 类型,则会在if语句块中,在此通过 MethodMatcher 进行匹配,匹配成功,则调用其中封装的原始拦截器的invoke方法,执行拦截器的逻辑,否则,递归调用proceed方法,每当proceed被再次执行时,this.currentInterceptorIndex的值是上一次的值自增过的,因此会进入下一个拦截器的执行。

如果当前拦截器不是 InterceptorAndDynamicMethodMatcher 类型,则调用它的invoke方法,执行拦截器的逻辑。

以上两种情况中,如果拦截器的逻辑被执行,也就是执行了拦截器的invoke方法,都会将this作为参数传入,this就是当前执行proceed方法的 ReflectiveMethodInvocation 对象,它是 MethodInvocation 接口的实现。

还有一点要说的是,我们常见的切面配置所封装成的拦截器,几乎都不是 InterceptorAndDynamicMethodMatcher 类型,因此,我们可以只分析else语句块中的情况,另外一种情况与之类似,只是增加了 MethodMatcher 再次匹配的步骤。

因此,我们分析的内容,可以简化成这样:

// org.springframework.aop.framework.ReflectiveMethodInvocation#proceed// 简化后的分析内容publicObjectproceed() throwsThrowable {
// We start with an index of -1 and increment early.if (this.currentInterceptorIndex==this.interceptorsAndDynamicMethodMatchers.size() -1) {
returninvokeJoinpoint();
   }
ObjectinterceptorOrInterceptionAdvice=this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

简化过后,可以很明显地看到三个步骤:

  1. 如果所有拦截器都执行完了,就调用目标方法。
  2. 获取下一个拦截器
  3. 执行获取到的拦截器

这是一个典型的递归写法,第一步的if语句,就是递归的结束条件,而后就是当前递归层次的处理逻辑。这里和一般的简单递归写法不同的是,它没有在方法体内部直接调用当前方法,但是,它执行了当前的过滤器的invoke方法,并且把this作为参数传进去了,而过滤器的invoke方法,在执行增强逻辑的过程中,还会调用thisproceed方法,也就是当前的这个proceed方法,执行下一层的拦截器逻辑。

以上就是对这个方法的整体逻辑分析,下面我们再看一下需要注意的几个细节。

递归终止条件

终止条件部分,第一个关键点是currentInterceptorIndex成员变量。

privateintcurrentInterceptorIndex=-1;

这里需要留意一下,它的初始值是-1,所以,它代表的并不是当前的proceed方法调用时要执行的拦截器的索引,进入方法的时候,之前已经执行过的拦截器的索引。也正因为此,下一步中获取当前要执行的拦截器时,使用的索引是++this.currentInterceptorIndex

接下来的一个重点就是invokeJoinpoint方法,这也是递归的最后一层,会执行的方法,也就是对目标方法的调用。我们进入这个方法看源码。

// org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint@NullableprotectedObjectinvokeJoinpoint() throwsThrowable {
returnAopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

这里调用了 AopUtils 类的invokeJoinpointUsingReflection方法,其实就是通过反射调用了目标方法,跟上一篇中分析的拦截器链为空的时候,执行的方法是同一个,我们就不深入分析了。

获取当前层次的拦截器

拦截器链中的逻辑是一层一层调用的,需要在目标方法执行前执行的增强逻辑自顶向下逐层执行完之后,就会执行目标方法,然后,再自底向上执行拦截器中需要在目标方法执行之后才执行的逻辑。因此,proceed方法的每一层递归,都对应了拦截器链中一个拦截器的处理,获取拦截器时候的get方法传入的索引++this.currentInterceptorIndex就是在每一层调用中增加索引的值,知道这个值到达拦截器链的末端。

拦截器增强逻辑的执行

获取到当前层的拦截器后,proceed方法的末尾执行了拦截器的invoke方法。拦截器链中的拦截器都是 MethodInterceptor 类型,虽然 5 种增强逻辑对应的拦截器的具体类型又不一样,但是它们都实现了 MethodInterceptor 的invoke方法,在这个方法中会执行拦截器中包含的增强逻辑,又因为invoke方法的参数中传入了this,所以invoke方法会在特定的时机再调用thisproceed方法。

所以,对拦截器链逐层执行就是通过递归调用proceed方法来实现的。

总结

本文分析了 ReflectiveMethodInvocation 类中的proceed方法,通过对这个方法的分析,了解了连接器链中的增强逻辑是如何逐层执行的,以及目标方法是什么时候被调用的。

下一篇,将分析 5 种增强类型对应的拦截器中invoke方法的具体原理。


目录
相关文章
|
7天前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
16 1
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
5天前
|
前端开发 Java 关系型数据库
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
|
5天前
|
前端开发 Java 编译器
详解Spring与JDK注入
依赖注入是Spring框架的核心概念之一,它通过将对象之间的依赖关系外部化,实现了松耦合和可测试性。面向切面编程则允许开发人员将横切关注点(如日志、事务管理)从应用程序的主要业务逻辑中分离出来,以提高代码的模块化和可维护性。
11 4
|
7天前
|
JSON 前端开发 Java
【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】Spring全家桶实现AOP-统一处理
6 0
|
7天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
12 0
|
7天前
|
Java Spring 容器
Spring AOP浅谈
Spring AOP浅谈
12 1
|
7天前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
23 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
7天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
17 2
|
7天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
28 2