文章目录
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
通知中执行方法前的逻辑。
具体的执行流程如下图所示:
定义切面类:
@Aspect@ComponentpublicclassLoggingAspect{@Before("execution(* com.example.service.*.*(..))")publicvoidlogBefore(JoinPoint joinPoint){System.out.println("Before method: "+ joinPoint.getSignature().getName());}@After("execution(* com.example.service.*.*(..))")publicvoidlogAfter(JoinPoint joinPoint){System.out.println("After method: "+ joinPoint.getSignature().getName());}}
配置切面扫描:
@Configuration@EnableAspectJAutoProxypublicclassAppConfig{// other bean definitions}
通过以上步骤,
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
方法:
源码提出来分析:
/**
* 从给定的 MetadataAwareAspectInstanceFactory 获取所有的 Advisor。
* 这个方法会处理切面类的方法和字段,创建相应的 Advisor 列表。
*
* @param aspectInstanceFactory MetadataAwareAspectInstanceFactory 实例,用于获取切面元数据和实例
* @return 包含所有 Advisor 的列表
*/@OverridepublicList<Advisor>getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory){// 获取切面类的元数据Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();// 获取切面类的名称String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();// 验证切面类是否合法validate(aspectClass);// 我们需要用一个装饰器包装 MetadataAwareAspectInstanceFactory,以便它只实例化一次MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =newLazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);// 创建一个保存 Advisor 的列表List<Advisor> advisors =newArrayList<>();// 遍历切面类的方法,获取所有符合条件的 Advisor 方法for(Method method :getAdvisorMethods(aspectClass)){// 在 Spring Framework 5.2.7 之前,advisors.size() 被作为 declarationOrderInAspect// 提供给 getAdvisor(...) 以表示在已声明方法列表中的“当前位置”。// 然而,自 Java 7 以来,“当前位置”已不再有效,因为 JDK 不再按源代码中声明的顺序返回声明的方法。// 因此,我们现在将 declarationOrderInAspect 硬编码为 0,以便通过反射发现的所有通知方法,// 以支持 JVM 启动期间的可靠通知排序。// 具体来说,值 0 与 AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor) 中使用的默认值对齐。Advisor advisor =getAdvisor(method, lazySingletonAspectInstanceFactory,0, aspectName);if(advisor !=null){// 将 Advisor 添加到列表中
advisors.add(advisor);}}// 如果它是每个目标实例化的切面,则发出虚拟实例化的切面if(!advisors.isEmpty()&& lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()){// 创建并添加一个合成的实例化 Advisor 到列表的开头Advisor instantiationAdvisor =newSyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);}// 查找引入字段for(Field field : aspectClass.getDeclaredFields()){// 获取 DeclareParents 的 AdvisorAdvisor advisor =getDeclareParentsAdvisor(field);if(advisor !=null){// 将 Advisor 添加到列表中
advisors.add(advisor);}}// 返回所有的 Advisorreturn advisors;}/**
* 从给定的方法和切面实例工厂获取一个 Advisor。
* 这个方法会处理切面类的方法并创建相应的 Advisor。
*
* @param candidateAdviceMethod 候选的通知方法
* @param aspectInstanceFactory MetadataAwareAspectInstanceFactory 实例,用于获取切面元数据和实例
* @param declarationOrderInAspect 在切面中的声明顺序
* @param aspectName 切面名称
* @return 对应的 Advisor,如果方法没有对应的 Pointcut 则返回 null
*/@Override@NullablepublicAdvisorgetAdvisor(Method candidateAdviceMethod,MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect,String aspectName){// 验证切面类是否合法validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());// 获取 PointcutAspectJExpressionPointcut expressionPointcut =getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if(expressionPointcut ==null){// 如果没有对应的 Pointcut,则返回 nullreturnnull;}// 创建并返回 InstantiationModelAwarePointcutAdvisorImplreturnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}/**
* 获取给定方法的 Pointcut。
*
* @param candidateAdviceMethod 候选的通知方法
* @param candidateAspectClass 候选的切面类
* @return 对应的 AspectJExpressionPointcut,如果方法没有对应的 AspectJ 注解则返回 null
*/@NullableprivateAspectJExpressionPointcutgetPointcut(Method candidateAdviceMethod,Class<?> candidateAspectClass){// 查找方法上的 AspectJ 注解AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if(aspectJAnnotation ==null){// 如果方法没有对应的 AspectJ 注解,则返回 nullreturnnull;}// 创建 AspectJExpressionPointcutAspectJExpressionPointcut ajexp =newAspectJExpressionPointcut(candidateAspectClass,newString[0],newClass<?>[0]);// 设置 Pointcut 表达式
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if(this.beanFactory !=null){// 如果 beanFactory 不为空,则设置 beanFactory
ajexp.setBeanFactory(this.beanFactory);}// 返回 AspectJExpressionPointcutreturn ajexp;}
这些源码的关键总结如下:
一. 获取所有 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 注解:创建
- 方法无 AspectJ 注解:跳过该方法。
- 切面是懒加载的:添加
SyntheticInstantiationAdvisor
到Advisor
列表开头。 - 遍历切面类的字段:检查每个字段是否包含
DeclareParents
注解。 - 字段有 DeclareParents 注解:创建相应的
Advisor
。
- 字段有 DeclareParents 注解:创建相应的
- 字段无 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
的配置。下面是一些关键代码片段:
publicclassAdvisedSupportextendsProxyConfigimplementsAdvised{// 持有的 TargetSource 实例privateTargetSource targetSource =EmptyTargetSource.INSTANCE;// 设置 TargetSourcepublicvoidsetTargetSource(TargetSource targetSource){this.targetSource =(targetSource !=null? targetSource :EmptyTargetSource.INSTANCE);}// 获取 TargetSourcepublicTargetSourcegetTargetSource(){returnthis.targetSource;}// 其他相关配置和方法}
ProxyFactory
类用于创建
AOP
代理。它继承自
AdvisedSupport
,所以可以直接访问
TargetSource
。
相关的类用一张图说明:
代码提出来分析
/**
* 根据此工厂中的设置创建一个新的代理。
* <p>此方法可以重复调用。如果我们添加或删除接口,效果会有所不同。
* 可以添加和删除拦截器。
* <p>使用默认的类加载器:通常是线程上下文类加载器(如果代理创建需要)。
* @return 代理对象
*/publicObjectgetProxy(){// 调用 createAopProxy() 方法创建 AOP 代理,然后调用其 getProxy() 方法返回代理对象returncreateAopProxy().getProxy();}/**
* 创建一个新的 AOP 代理。子类应调用此方法来获取新的 AOP 代理,而不应直接使用 this 作为参数创建 AOP 代理。
* @return 新的 AOP 代理
*/protectedfinalsynchronizedAopProxycreateAopProxy(){// 如果代理还没有激活,则激活代理if(!this.active){activate();}// 调用 AopProxyFactory 的 createAopProxy 方法创建新的 AOP 代理returngetAopProxyFactory().createAopProxy(this);}/**
* 根据 AdvisedSupport 配置创建一个 AOP 代理。此方法决定使用哪种代理实现(JDK 动态代理或 CGLIB 代理)。
**/@OverridepublicAopProxycreateAopProxy(AdvisedSupport config)throwsAopConfigException{// 检查是否在原生镜像中执行,以及是否优化、代理目标类或没有用户提供的代理接口if(!NativeDetector.inNativeImage()&&(config.isOptimize()|| config.isProxyTargetClass()||hasNoUserSuppliedProxyInterfaces(config))){// 获取目标类Class<?> targetClass = config.getTargetClass();// 如果目标类为空,抛出 AopConfigException 异常if(targetClass ==null){thrownewAopConfigException("TargetSource 无法确定目标类: "+"创建代理需要一个接口或目标对象。");}// 如果目标类是接口或代理类或 lambda 表达式,使用 JdkDynamicAopProxyif(targetClass.isInterface()||Proxy.isProxyClass(targetClass)||AopProxyUtils.isLambda(targetClass)){returnnewJdkDynamicAopProxy(config);}// 否则,使用 ObjenesisCglibAopProxyreturnnewObjenesisCglibAopProxy(config);}else{// 默认使用 JdkDynamicAopProxyreturnnewJdkDynamicAopProxy(config);}}
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-----------------------
版权归原作者 砖业洋__ 所有, 如有侵权,请联系我们删除。