Spring 源码阅读 63:基于 JDK 的 AOP 代理如何获取拦截器链(1)- 拦截器链的来源

简介: 本文分析了 JdkDynamicAopProxy 在执行代理的回调逻辑时,如何根据目标方法找到其对应的拦截器链。

本文分析了 JdkDynamicAopProxy 在执行代理的回调逻辑时,如何根据目标方法找到其对应的拦截器链。

基于 Spring Framework v5.2.6.RELEASE

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

接上一篇:Spring 源码阅读 62:基于 JDK 的 AOP 代理对特殊方法调用的处理

概述

上一篇分析了 JdkDynamicAopProxy 的invoke回调方法对特殊目标方法调用的处理,本文开始,会接着分析invoke方法的下一个关键步骤,即拦截器链的获取。为保证内容的连续性,推荐先阅读文章开头列出的两篇文章。

获取拦截器链

拦截器链的获取,对应invoke方法的下面这段代码。

// Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target=targetSource.getTarget();
Class<?>targetClass= (target!=null?target.getClass() : null);
// Get the interception chain for this method.List<Object>chain=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

获取拦截器链的步骤,通过advisedgetInterceptorsAndDynamicInterceptionAdvice方法来完成,调用方法是需要目标类型和被调用的目标方法作为参数。这个方法声明在 AdvisedSupport 中。我们找到方法的实现。

拦截器链的缓存

// org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvicepublicList<Object>getInterceptorsAndDynamicInterceptionAdvice(Methodmethod, @NullableClass<?>targetClass) {
MethodCacheKeycacheKey=newMethodCacheKey(method);
List<Object>cached=this.methodCache.get(cacheKey);
if (cached==null) {
cached=this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
   }
returncached;
}

虽然方法声明的返回值是 Object 列表,但注释是这样描述的:

Determine a list of org.aopalliance.intercept.MethodInterceptor objects for the given method, based on this configuration.

也就是说,返回的是一个 MethodInterceptor 列表。

在方法体中,先通过method获取了一个cacheKey,然后到methodCache中去查找已经缓存的拦截器列表,如果有,则直接返回,如果没有,则通过advisorChainFactorygetInterceptorsAndDynamicInterceptionAdvice方法获取,然后放入缓存中再返回。

MethodCacheKey 是 AdvisedSupport 的内部类,初始化cacheKey的方法如下。

// org.springframework.aop.framework.AdvisedSupport.MethodCacheKey#MethodCacheKeypublicMethodCacheKey(Methodmethod) {
this.method=method;
this.hashCode=method.hashCode();
}

可以看出,这个方法核心逻辑是由getInterceptorsAndDynamicInterceptionAdvice完成的,增加缓存机制是为了确保针对每个方法查找拦截器链的逻辑只会执行一次。

这里还需要看一下调用上述方法的成员变量advisorChainFactory

/** The AdvisorChainFactory to use. */AdvisorChainFactoryadvisorChainFactory=newDefaultAdvisorChainFactory();

其实就是一个 DefaultAdvisorChainFactory 类型的对象,并且没有在类中定义构造方法。因此,下面我们直接看它的getInterceptorsAndDynamicInterceptionAdvice方法。

拦截器链的获取

image.png

方法的代码量比较大,我们先做整体结构的分析。

首先,在for循环之前的部分,定义了一些变量,这些都是后面具体的处理逻辑中需要用到的。其中,interceptorList是方法最终的返回结果。

接下来,方法的处理逻辑都在for循环中,for循环遍历了创建代理对象时配置的 Advisor 列表,在循环体中,针对 Advisor 的具体类型(PointcutAdvisor 或 IntroductionAdvisor 以及其他)执行不同的处理逻辑,每一种处理逻辑的最后,都是向方法返回值interceptorList列表中添加内容。

最终,interceptorList作为返回值返回。

下面进入详细分析,先看方法体的第一部分。

// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.AdvisorAdapterRegistryregistry=GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors=config.getAdvisors();
List<Object>interceptorList=newArrayList<>(advisors.length);
Class<?>actualClass= (targetClass!=null?targetClass : method.getDeclaringClass());
BooleanhasIntroductions=null;

我们逐个分析这几个变量。首先是 AdvisorAdapterRegistry 类型的registry,找到 GlobalAdvisorAdapterRegistry 的getInstance方法。

/*** Keep track of a single instance so we can return it to classes that request it.*/privatestaticAdvisorAdapterRegistryinstance=newDefaultAdvisorAdapterRegistry();
/*** Return the singleton {@link DefaultAdvisorAdapterRegistry} instance.*/publicstaticAdvisorAdapterRegistrygetInstance() {
returninstance;
}

可以看出,它是一个全局单例的 DefaultAdvisorAdapterRegistry 类型的对象个。这个单例的 DefaultAdvisorAdapterRegistry 对象其实在以前的代码分析中见过,在 AbstractAutoProxyCreator 类的buildAdvisors方法中,就曾经通过它的wrap方法来封装 Advisor。根据这里获取它的方式,可以知道这两个地方用到的是同一个对象。

言归正传,再看方法中的advisors变量,它是之前创建代理对象的时候,配置的 Advisor 列表,也是后续for循环遍历的列表。

第三个变量interceptorList是方法最终返回的结果列表,初始状态为空,在后面的for循环中会不断向这个列表中添加元素。

第四个变量actualClass是当前被调用目标方法所在的类型。

分析到这里,也可以大概猜出后续的主要逻辑,就是根据advisors中的每一个 Advisor,来得到所有的拦截器,并放入列表中返回。这一部分,是这个方法的核心逻辑,也是一块相对完整且复杂的逻辑,我会放到下一篇中单独分析。

总结

本文分析了 JdkDynamicAopProxy 在执行代理的回调逻辑时,如何根据目标方法找到其对应的拦截器链。对于 Advisor 是如何转换成 Intercepter 拦截器的,下一篇会详细分析。

目录
相关文章
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
21小时前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
21 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
7天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
15 2
|
7天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
20 2
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
54 0
|
3月前
|
缓存 Java Maven
Spring Boot自动配置原理
Spring Boot自动配置原理
52 0
|
2月前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
138 0
|
1月前
|
存储 JSON Java
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
45 2
|
2月前
|
前端开发 搜索推荐 Java
【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革
【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革
|
1月前
|
前端开发 Java 应用服务中间件
Springboot对MVC、tomcat扩展配置
Springboot对MVC、tomcat扩展配置