Spring 高手之路 23——AOP 触发机制与代理逻辑的执行
1. 从整体视角学习 Bean 是如何被 AOP 代理的
为了全面理解Bean
是如何被AOP
代理的,我们把前面几篇文章串联一下,需要关注以下几点,并针对每个关键点学习相应的源码部分:
1. AOP 代理的触发机制(本章需要讲解的)理解Spring
如何决定哪些Bean
需要被代理。
关键点:
BeanPostProcessor 接口:
Spring AOP
的自动代理创建器实现了这个接口,通过它在Bean
初始化的前后进行处理。
源码部分:AbstractAutoProxyCreator
(位于org.springframework.aop.framework.autoproxy
包中)
方法:
postProcessAfterInitialization
方法:
wrapIfNecessary
2. 确定哪些 Bean 需要代理(前面已讲)理解Spring
如何确定哪些Bean
需要被代理,这通常涉及到扫描和匹配切面。
这部分内容在 Spring 高手之路 21——深入剖析 Spring AOP 代理对象的创建的第
2
节“匹配增强器Advisors
(源码分析+时序图说明)”已经讲解,可自行前往翻阅学习。
关键点:
Advisor 和 Advice:这些是
Spring AOP
中的切面和通知。
源码部分:
AbstractAdvisorAutoProxyCreator 的方法:
getAdvicesAndAdvisorsForBean
(位于org.springframework.aop.framework.autoproxy
包中)
3. 创建代理对象(前面已讲)理解Spring
如何创建代理对象,包括使用JDK
动态代理和CGLIB
代理。
这部分内容在 Spring 高手之路 22——AOP 切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
代理工厂(ProxyFactory):用于创建代理对象。
动态代理实现(
JDK
动态代理和CGLIB
)。
源码部分:
ProxyFactory 的方法:
getProxy
(位于org.springframework.aop.framework
包中)JdkDynamicAopProxy 的方法:
invoke
(位于org.springframework.aop.framework
包中)CglibAopProxy 的方法:
getProxy
(位于org.springframework.aop.framework
包中)
4. 代理对象的配置(前面已讲)理解如何配置代理对象的属性,如切入点和通知。
这部分内容在 Spring 高手之路 22——AOP 切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
AdvisedSupport 类:包含了代理配置的相关信息。
源码部分:
AdvisedSupport
(位于org.springframework.aop.framework
包中)
5. 执行代理逻辑(本章需要讲解的)理解代理对象在运行时如何拦截方法调用并执行通知逻辑。
关键点:
拦截器链:代理对象通过拦截器链来执行切面逻辑。
源码部分:
ReflectiveMethodInvocation 的方法:
proceed
(位于org.springframework.aop.framework
包中)
推荐学习路径
从高层理解代理触发机制
查看
AbstractAutoProxyCreator
类,特别是wrapIfNecessary
和postProcessAfterInitialization
方法,理解代理触发的整体流程。
深入理解切面和通知的获取
查看
AbstractAdvisorAutoProxyCreator
类,特别是getAdvicesAndAdvisorsForBean
方法,理解如何获取适用于目标Bean
的切面和通知。
代理对象的创建细节
查看
ProxyFactory
类,特别是getProxy
方法,理解代理对象的具体创建过程。
理解动态代理的实现
查看
JdkDynamicAopProxy
和CglibAopProxy
类,理解JDK
动态代理和CGLIB
代理的具体实现细节。
配置代理对象
查看
AdvisedSupport
类,理解代理对象的配置和管理。
执行代理逻辑
查看
ReflectiveMethodInvocation
类,特别是proceed
方法,理解代理对象在运行时如何拦截方法调用并执行切面逻辑。
2. AOP 代理的触发机制
还记得BeanPostProcessor
接口吗?这在“Spring 高手之路 13——BeanFactoryPostProcessor 与 BeanDefinitionRegistryPostProcessor 解析”有讲到。
BeanPostProcessor
接口提供了两个方法:
postProcessBeforeInitialization:在
Bean
初始化之前调用。postProcessAfterInitialization:在
Bean
初始化之后调用。
Spring AOP
利用postProcessAfterInitialization
方法在Bean
初始化完成后,检查并决定是否需要对这个Bean
进行代理。
2.1 postProcessAfterInitialization 方法源码分析
本节源码基于 spring-aop-5.3.16
。
代码提出来分析
这个方法主要功能:
确定代理需求:该方法的主要目的是确定是否需要为
Bean
创建代理。如果需要,wrapIfNecessary
方法将负责实际的代理创建过程。优化性能:通过缓存键和
earlyProxyReferences
集合,可以避免重复处理同一个Bean
,提高性能。集成 AOP 功能:为需要代理的
Bean
创建代理对象,使得AOP
切面能够在Bean
的方法调用前后执行。
2.2 wrapIfNecessary 方法源码分析
代码提出来分析
wrapIfNecessary
方法的主要功能是检查给定的Bean
是否需要进行AOP
代理,并在必要时为其创建代理对象。该方法在Bean
初始化后被调用,以确保符合条件的Bean
能够被AOP
代理,从而使AOP
切面能够在Bean
的方法调用前后执行。
2.3 时序图演示触发机制
详细分析和说明:
Caller 请求 Bean 实例:
Caller
调用BeanFactory
来获取Bean
实例。这一步表示应用程序或其他
Bean
调用者需要获取一个Bean
实例。
BeanFactory 创建 Bean 实例:
BeanFactory
创建Bean
实例,准备进行初始化。这包括通过
Spring
的依赖注入机制构造Bean
实例。
调用 postProcessAfterInitialization:
BeanFactory
调用AOPProxy
的postProcessAfterInitialization
方法。这是
Spring AOP
框架用于在Bean
初始化后进行处理的关键步骤。
获取缓存键:
AOPProxy
获取缓存键(cacheKey
),通常是Bean
的类和名称的组合。缓存键用于在缓存中存取与
Bean
相关的元数据。
检查 earlyProxyReferences 集合:
AOPProxy
检查earlyProxyReferences
集合,判断当前Bean
是否已经处理过。这是为了避免重复处理同一个
Bean
,提高性能。
判断 Bean 是否需要代理:
通过检查
earlyProxyReferences
集合,决定是否需要继续代理。如果
Bean
需要代理,进入wrapIfNecessary
方法。
调用 wrapIfNecessary:
wrapIfNecessary
方法负责实际的代理创建过程。包括进一步的检查和代理对象的创建。
检查 targetSourcedBeans 集合:
判断
Bean
是否在 targetSourcedBeans 集合中,如果是,直接返回原始Bean
。targetSourcedBeans
集合用于存储一些特殊的Bean
,不需要代理。
targetSourcedBeans
集合主要用于管理那些使用动态目标源的Bean
。在创建代理时,如果一个Bean
在这个集合中,Spring
会进行特殊处理,通常直接返回原始Bean
,而不是创建新的代理对象。这种机制确保了特殊Bean
的正确处理和高效运行。
检查 advisedBeans 集合:
检查缓存中是否已经标记了该
Bean
不需要代理,如果是,直接返回原始Bean
。提高处理效率,避免重复检查。
检查基础设施类和跳过条件:
判断
Bean
是否为Spring
的基础设施类或是否有其他跳过条件。基础设施类通常不需要代理,因为它们是框架内部使用的。
获取通知和切面:
调用
getAdvicesAndAdvisorsForBean
方法,获取适用于该Bean
的通知(Advice
)和切面(Advisor
)。Advisor
对象包含了切面逻辑,Advice
对象包含了实际的通知逻辑。
检查是否有 specificInterceptors:
判断是否有适用于该
Bean
的通知和切面。如果有,继续创建代理对象。
创建代理对象:
调用
createProxy
方法,通过ProxyFactory
创建代理对象。代理对象将拦截方法调用,并应用切面逻辑。
返回代理对象:
代理对象创建完成后,返回给
AOPProxy
。AOPProxy
将代理对象返回给BeanFactory
。
返回原始
Bean
或代理对象:
如果
Bean
不需要代理,返回原始Bean
。如果需要代理,返回代理对象。
BeanFactory
返回结果:
BeanFactory
将最终的Bean
实例(可能是原始Bean
或代理对象)返回给Caller
。
3. AOP 代理逻辑的执行
3.1 AOP 代理如何使用拦截器
当AOP
代理拦截方法调用时,它会依次调用配置的拦截器链。每个拦截器都通过invoke
方法处理方法调用。整个流程如下:
方法调用被代理拦截:当一个方法被调用时,
AOP
代理会首先拦截这个调用。拦截器链处理:调用会被传递给拦截器链中的第一个拦截器。
执行拦截器链:每个拦截器的
invoke
方法都会执行其逻辑,并决定是否调用下一个拦截器。目标方法执行:如果所有拦截器都允许,最终会调用目标方法本身。
返回结果或处理异常:目标方法执行完成后,结果或异常会逐个传递回拦截器链,直到返回给最初调用方法的客户端。
比如给个简单拦截器的例子:
在这个示例中,invoke
方法是拦截器的核心。
当方法被调用时,
invoke
方法会先打印“方法调用前”,然后调用invocation.proceed()
执行目标方法,最后在方法执行后或发生异常时打印相应的信息。invocation.proceed()
方法用于继续调用拦截器链中的下一个拦截器,直到最终调用目标方法。
3.2 proceed 方法源码分析
代码提出来分析:
proceed
方法和invokeJoinpoint
方法共同实现了AOP
拦截器链的执行逻辑。proceed
方法依次执行拦截器链中的每个拦截器,并在链的末尾调用目标方法。invokeJoinpoint
方法通过反射调用目标方法并返回其结果。这两个方法结合起来,使得AOP
能够在目标方法执行前后插入各种横切关注点,如事务管理、日志记录等。
这里判断动态方法匹配器是干嘛的?
静态方法匹配器:在编译时或配置时确定方法是否匹配,例如方法名、参数类型等。这种匹配是静态的,无法根据实际调用时的参数值进行判断。
动态方法匹配器:在方法调用时动态评估方法匹配条件,例如根据实际传递的参数值判断是否匹配。这种匹配是动态的,可以提供更灵活的控制。
普通的AOP
通常使用静态匹配,这里不用深究动态匹配器。
3.3 时序图
时序图详细说明:
Caller 调用代理方法:
Caller
调用代理对象的方法,开始AOP
的执行过程。代理对象是通过
AOP
创建的,包含了拦截器链。
创建 ReflectiveMethodInvocation 实例:
在代理对象内部,
Spring AOP
创建一个ReflectiveMethodInvocation
实例。该实例封装了目标方法、拦截器链以及方法参数。ReflectiveMethodInvocation
负责处理方法调用的整个过程。
调用 proceed():
代理对象调用
ReflectiveMethodInvocation
的proceed()
方法,开始执行拦截器链。
遍历拦截器链:
ReflectiveMethodInvocation
遍历拦截器链中的每个拦截器。对每个拦截器,
ReflectiveMethodInvocation
调用其invoke(this)
方法。拦截器在调用目标方法之前和之后执行自定义逻辑。
调用 invokeJoinpoint():
如果拦截器链中没有拦截器或所有拦截器已经执行完毕,
ReflectiveMethodInvocation
直接调用invokeJoinpoint()
方法。invokeJoinpoint()
通过反射调用目标方法。
调用目标方法:
ReflectiveMethodInvocation
通过反射调用实际的目标方法。目标方法执行并返回结果。
返回结果:
目标方法执行完成后,结果返回给
ReflectiveMethodInvocation
。ReflectiveMethodInvocation
将结果返回给所有拦截器,依次向上返回。
代理对象返回最终结果:
ReflectiveMethodInvocation
最终将结果返回给代理对象。代理对象将结果返回给
Caller
。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
版权声明: 本文为 InfoQ 作者【砖业洋__】的原创文章。
原文链接:【http://xie.infoq.cn/article/d7c39339f968c1c1ab1c6d85c】。
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明。
评论