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】协议,转载请保留原文出处及本版权声明。









评论