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 拦截器的,下一篇会详细分析。

目录
相关文章
|
18天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
21天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
19小时前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
7 2
|
3天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
4天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
46 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
12天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
16天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
22天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0
|
23天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
34 0
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
23天前
|
人工智能 监控 安全
Java+Spring Cloud +Vue+UniApp微服务智慧工地云平台源码
视频监控系统、人员实名制与分账制管理系统、车辆管理系统、环境监测系统、大型设备监测(龙门吊、塔吊、升降机、卸料平台等)、用电监测系统、基坑监测系统、AI算法分析(安全帽佩戴、火焰识别、周界报警、人员聚众报警、升降机超载报警)、安全培训、设备监测。
28 4