Spring 高手之路 22——AOP 切面类的封装与解析

1. AOP 是如何收集切面类并封装的?
在 Spring 中,AOP(Aspect-Oriented Programming,面向切面编程)通过以下几个步骤收集切面类并进行封装:
1. 定义切面类:
切面类通过
@Aspect注解来标记,表示这是一个切面。在切面类中定义通知(
advice),例如@Before、@After、@Around等,用于指定在目标方法执行的不同阶段要执行的逻辑。
2. 配置切面扫描:
Spring容器在启动时,会根据配置或注解扫描包路径,寻找带有@Aspect注解的类。通过在配置类上添加
@EnableAspectJAutoProxy注解启用AOP自动代理机制。
3. 解析切面类:
Spring使用AspectJAnnotationAutoProxyCreator或类似的类来扫描和解析@Aspect注解的类。在解析过程中,
Spring通过反射机制查找切面类中定义的通知方法,并解析这些方法上面的注解,例如@Before、@After等。
4. 创建切面和通知对象:
解析切面类后,
Spring会为每个切面创建AspectMetadata对象,并将这些元数据保存在AdvisedSupport类中。AdvisedSupport持有目标对象(被代理的类)和通知(advice)列表。
5. 织入切面:
在
Spring容器创建bean的过程中,如果发现某个bean需要AOP功能,Spring会为其创建代理对象。Spring使用ProxyFactory或类似的工厂类,根据目标对象和通知列表创建代理对象。代理对象会将通知逻辑织入到目标方法的调用链中。
6. 执行代理逻辑:
当客户端代码调用代理对象的方法时,代理对象会首先执行切面中的通知逻辑,然后再调用目标方法。
例如,在
@Around通知中,可以在目标方法执行前后执行额外的逻辑,或者在@Before通知中执行方法前的逻辑。
具体的执行流程如下图所示:
定义切面类:
配置切面扫描:
通过以上步骤,Spring AOP 机制能够自动收集、解析和织入切面类,实现面向切面编程的功能。这种机制在保持业务逻辑和横切关注点分离的同时,也提升了代码的可读性和可维护性。
2. Advisor 是什么,它是怎么构建的?
2.1 什么是 Advisor
在 Spring AOP 中,Advisor 是包含 Advice 和 Pointcut 的一个概念。Advice 是要执行的具体动作(如方法前、后或环绕方法),而 Pointcut 是定义哪些连接点应该应用这些 Advice 的规则。
2.2 Advisor 的构建(源码分析+时序图说明)
本节源码基于 spring-aop-5.3.16。
在 Spring AOP 中,Advisor 的构建是通过 AspectJAdvisorFactory 来实现的。ReflectiveAspectJAdvisorFactory 是 AspectJAdvisorFactory 的一个具体实现。
来看看ReflectiveAspectJAdvisorFactory 的 getAdvisors 方法:
源码提出来分析:
这些源码的关键总结如下:
一. 获取所有 Advisor (getAdvisors 方法)1. 提取切面元数据:
获取切面类和切面名称,这些信息是从
MetadataAwareAspectInstanceFactory中提取的。
2. 装饰工厂:
用
LazySingletonAspectInstanceFactoryDecorator包装原始工厂,这样可以确保切面实例只会被创建一次(懒加载)。
3. 处理通知方法:
遍历切面类中的所有方法,检查每个方法是否是通知方法(如
@Before、@After等)。对每个通知方法调用
getAdvisor方法,创建相应的 Advisor 对象,并将其添加到列表中。getAdvisor方法通过解析方法上的注解来创建AspectJExpressionPointcut,然后将其与通知方法一起封装成InstantiationModelAwarePointcutAdvisorImpl对象。
4. 处理懒加载的切面:
如果切面类是懒加载的,则添加一个
SyntheticInstantiationAdvisor到Advisor列表的开头,以确保在真正使用切面之前完成实例化。
5. 处理引入增强:
遍历切面类中的所有字段,检查是否有引入增强(
@DeclareParents注解)。对每个引入增强字段,调用
getDeclareParentsAdvisor方法,创建相应的Advisor对象,并将其添加到列表中。
6. 返回所有 Advisor:
最终,返回包含所有
Advisor对象的列表,这些Advisor将用于在目标对象上应用切面逻辑。
二. 获取单个 Advisor (getAdvisor 方法)
1. 验证切面类:
确保传入的切面类是有效的切面。
2. 获取 Pointcut:
通过解析方法上的
AspectJ注解(如@Before、@After),创建AspectJExpressionPointcut对象。如果方法没有相应的注解,则返回
null。
3. 创建 Advisor:
将
AspectJExpressionPointcut和通知方法封装成InstantiationModelAwarePointcutAdvisorImpl对象。返回创建的
Advisor对象。
三. 获取 Pointcut (getPointcut 方法)
1. 查找方法上的 AspectJ 注解:
检查给定的方法是否有
AspectJ注解(如@Before、@After)。
2. 创建 Pointcut:
如果找到了
AspectJ注解,则创建AspectJExpressionPointcut对象,并设置相应的切入点表达式。如果存在
beanFactory,则设置beanFactory。
3. 返回 Pointcut:
返回配置好的
AspectJExpressionPointcut对象,如果没有找到注解,则返回null。
简要总结
getAdvisors方法从切面工厂中提取元数据,装饰工厂以实现懒加载,处理切面类中的通知方法和引入增强,最终返回所有的Advisor。getAdvisor方法根据通知方法创建相应的Advisor,并将其与Pointcut结合起来。getPointcut方法通过解析方法上的注解来创建并配置Pointcut,用于在指定的连接点上应用通知。
时序图如下:
关键步骤说明
获取所有 Advisor:由
Spring容器发起,调用ReflectiveAspectJAdvisorFactory的getAdvisors方法。获取切面类和切面名称:从
MetadataAwareAspectInstanceFactory中提取切面类和切面名称。验证切面类是否合法:确保切面类是有效的。
包装工厂以实现懒加载:使用
LazySingletonAspectInstanceFactoryDecorator包装工厂。遍历切面类的方法:逐个检查每个方法,是否包含
AspectJ注解。方法有 AspectJ 注解:创建
AspectJExpressionPointcut,并与方法封装成InstantiationModelAwarePointcutAdvisorImpl。方法无 AspectJ 注解:跳过该方法。
切面是懒加载的:添加
SyntheticInstantiationAdvisor到Advisor列表开头。遍历切面类的字段:检查每个字段是否包含
DeclareParents注解。字段有 DeclareParents 注解:创建相应的
Advisor。字段无 DeclareParents 注解:跳过该字段。
返回所有 Advisor:将包含所有
Advisor的列表返回给Spring容器。
3. TargetSource 的构建和作用
在 Spring AOP 中,TargetSource 是一个重要的概念,它用于描述 AOP 代理的目标对象。目标对象是被代理对象,即实际执行业务逻辑的对象。TargetSource 定义了如何获取这个目标对象以及它的生命周期管理。
3.1 TargetSource 的作用
获取目标对象:
TargetSource提供了获取目标对象的机制,代理对象在执行方法调用时,通过TargetSource获取实际的目标对象。目标对象的生命周期管理:
TargetSource负责目标对象的生命周期管理,比如创建、缓存、销毁等。支持多种代理模式:通过不同的
TargetSource实现,可以支持不同的代理模式,比如单例、多例、懒加载等。
常见的 TargetSource 实现
1. SingletonTargetSource:单例目标源,目标对象是单例的,每次获取的都是同一个实例。
2. PrototypeTargetSource:原型目标源,每次获取目标对象时,都会创建一个新的实例。
3. LazyInitTargetSource:懒加载目标源,目标对象只有在第一次使用时才会被创建。
3.2 TargetSource 的构建
核心类与方法
AdvisedSupport:这个类持有
TargetSource和其他AOP配置。ProxyFactory:这个类用于创建
AOP代理。AopProxy:具体的代理实现类(如
JdkDynamicAopProxy和CglibAopProxy)。
AdvisedSupport 是 Spring AOP 框架中一个核心的配置类,它持有 TargetSource,以及其他关于 AOP 的配置。下面是一些关键代码片段:
ProxyFactory 类用于创建 AOP 代理。它继承自 AdvisedSupport,所以可以直接访问 TargetSource。
相关的类用一张图说明:
代码提出来分析
getProxy 方法是调用 createAopProxy() 方法创建 AOP 代理对象,然后调用创建的 AOP 代理对象的 getProxy() 方法返回实际的代理对象。
createAopProxy 方法首先检查代理是否已激活,如果未激活则调用 activate() 方法激活代理。调用 getAopProxyFactory().createAopProxy(this) 方法,通过 AopProxyFactory 创建新的 AOP 代理对象。
createAopProxy 方法过程如下:
检查是否在原生镜像中执行,以及是否需要优化、代理目标类或没有用户提供的代理接口。
获取目标类,如果目标类为空,抛出
AopConfigException异常。如果目标类是接口、代理类或
lambda表达式,使用JdkDynamicAopProxy创建代理。否则,使用
ObjenesisCglibAopProxy创建代理。如果不满足上述条件,默认使用
JdkDynamicAopProxy。
下面时序图是说明 TargetSource 在 Spring AOP 中的构建和使用过程:
关键步骤说明
1. 创建并配置 AdvisedSupport 实例:
Spring容器创建一个AdvisedSupport实例,并根据配置设置其属性,包括TargetSource。
2. 设置 TargetSource:
在
AdvisedSupport实例中设置TargetSource,它定义了如何获取目标对象。
3. 创建 AopProxy:
Spring容器调用AopProxyFactory创建AopProxy实例,传递AdvisedSupport作为配置。
4. 获取配置信息:
AopProxyFactory从AdvisedSupport获取创建AopProxy所需的配置信息。
5. 根据配置创建 AopProxy 实例:
AopProxyFactory根据AdvisedSupport配置决定使用哪种AopProxy实现(如JdkDynamicAopProxy或CglibAopProxy),并创建AopProxy实例。
6. 获取 TargetSource:
AopProxy从AdvisedSupport中获取TargetSource实例。
7. 获取目标对象:
AopProxy调用TargetSource的getTarget方法获取实际的目标对象实例。
8. 创建代理对象:
AopProxy使用目标对象创建代理对象。
9. 返回代理对象:
AopProxy将创建的代理对象返回给Spring容器。
10. 返回代理对象给调用者:
Spring容器将代理对象返回给调用者。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
版权声明: 本文为 InfoQ 作者【砖业洋__】的原创文章。
原文链接:【http://xie.infoq.cn/article/c3925b8d231d305d77b18ec99】。文章转载请联系作者。







评论