0


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

文章目录

1. AOP是如何收集切面类并封装的?

  1. Spring

中,

  1. AOP

  1. 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 通知中执行方法前的逻辑。

具体的执行流程如下图所示:

定义切面类:

  1. @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());}}

配置切面扫描:

  1. @Configuration@EnableAspectJAutoProxypublicclassAppConfig{// other bean definitions}

  通过以上步骤,

  1. Spring AOP

机制能够自动收集、解析和织入切面类,实现面向切面编程的功能。这种机制在保持业务逻辑和横切关注点分离的同时,也提升了代码的可读性和可维护性。

2. Advisor 是什么,它是怎么构建的?

2.1 什么是 Advisor

  在

  1. Spring AOP

中,

  1. Advisor

是包含

  1. Advice

  1. Pointcut

的一个概念。

  1. Advice

是要执行的具体动作(如方法前、后或环绕方法),而

  1. Pointcut

是定义哪些连接点应该应用这些

  1. Advice

的规则。

2.2 Advisor 的构建(源码分析+时序图说明)

本节源码基于

  1. spring-aop-5.3.16

  在

  1. Spring AOP

中,

  1. Advisor

的构建是通过

  1. AspectJAdvisorFactory

来实现的。

  1. ReflectiveAspectJAdvisorFactory

  1. AspectJAdvisorFactory

的一个具体实现。

来看看

  1. ReflectiveAspectJAdvisorFactory

  1. getAdvisors

方法:

在这里插入图片描述

源码提出来分析:

  1. /**
  2. * 从给定的 MetadataAwareAspectInstanceFactory 获取所有的 Advisor。
  3. * 这个方法会处理切面类的方法和字段,创建相应的 Advisor 列表。
  4. *
  5. * @param aspectInstanceFactory MetadataAwareAspectInstanceFactory 实例,用于获取切面元数据和实例
  6. * @return 包含所有 Advisor 的列表
  7. */@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 添加到列表中
  8. advisors.add(advisor);}}// 如果它是每个目标实例化的切面,则发出虚拟实例化的切面if(!advisors.isEmpty()&& lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()){// 创建并添加一个合成的实例化 Advisor 到列表的开头Advisor instantiationAdvisor =newSyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
  9. advisors.add(0, instantiationAdvisor);}// 查找引入字段for(Field field : aspectClass.getDeclaredFields()){// 获取 DeclareParents 的 AdvisorAdvisor advisor =getDeclareParentsAdvisor(field);if(advisor !=null){// 将 Advisor 添加到列表中
  10. advisors.add(advisor);}}// 返回所有的 Advisorreturn advisors;}/**
  11. * 从给定的方法和切面实例工厂获取一个 Advisor
  12. * 这个方法会处理切面类的方法并创建相应的 Advisor
  13. *
  14. * @param candidateAdviceMethod 候选的通知方法
  15. * @param aspectInstanceFactory MetadataAwareAspectInstanceFactory 实例,用于获取切面元数据和实例
  16. * @param declarationOrderInAspect 在切面中的声明顺序
  17. * @param aspectName 切面名称
  18. * @return 对应的 Advisor,如果方法没有对应的 Pointcut 则返回 null
  19. */@Override@NullablepublicAdvisorgetAdvisor(Method candidateAdviceMethod,MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect,String aspectName){// 验证切面类是否合法validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());// 获取 PointcutAspectJExpressionPointcut expressionPointcut =getPointcut(
  20. candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if(expressionPointcut ==null){// 如果没有对应的 Pointcut,则返回 nullreturnnull;}// 创建并返回 InstantiationModelAwarePointcutAdvisorImplreturnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}/**
  21. * 获取给定方法的 Pointcut
  22. *
  23. * @param candidateAdviceMethod 候选的通知方法
  24. * @param candidateAspectClass 候选的切面类
  25. * @return 对应的 AspectJExpressionPointcut,如果方法没有对应的 AspectJ 注解则返回 null
  26. */@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 表达式
  27. ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if(this.beanFactory !=null){// 如果 beanFactory 不为空,则设置 beanFactory
  28. ajexp.setBeanFactory(this.beanFactory);}// 返回 AspectJExpressionPointcutreturn ajexp;}

这些源码的关键总结如下:

一. 获取所有 Advisor (getAdvisors 方法)
1. 提取切面元数据:

  • 获取切面类和切面名称,这些信息是从 MetadataAwareAspectInstanceFactory 中提取的。

2. 装饰工厂:

  • LazySingletonAspectInstanceFactoryDecorator 包装原始工厂,这样可以确保切面实例只会被创建一次(懒加载)。

3. 处理通知方法:

  • 遍历切面类中的所有方法,检查每个方法是否是通知方法(如 @Before@After 等)。
  • 对每个通知方法调用 getAdvisor 方法,创建相应的 Advisor 对象,并将其添加到列表中。
  • getAdvisor 方法通过解析方法上的注解来创建 AspectJExpressionPointcut,然后将其与通知方法一起封装成 InstantiationModelAwarePointcutAdvisorImpl 对象。

4. 处理懒加载的切面:

  • 如果切面类是懒加载的,则添加一个 SyntheticInstantiationAdvisorAdvisor 列表的开头,以确保在真正使用切面之前完成实例化。

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 容器发起,调用 ReflectiveAspectJAdvisorFactorygetAdvisors 方法。
  • 获取切面类和切面名称:从 MetadataAwareAspectInstanceFactory 中提取切面类和切面名称。
  • 验证切面类是否合法:确保切面类是有效的。
  • 包装工厂以实现懒加载:使用 LazySingletonAspectInstanceFactoryDecorator 包装工厂。
  • 遍历切面类的方法:逐个检查每个方法,是否包含 AspectJ 注解。
    • 方法有 AspectJ 注解:创建 AspectJExpressionPointcut,并与方法封装成 InstantiationModelAwarePointcutAdvisorImpl
    • 方法无 AspectJ 注解:跳过该方法。
  • 切面是懒加载的:添加 SyntheticInstantiationAdvisorAdvisor 列表开头。
  • 遍历切面类的字段:检查每个字段是否包含 DeclareParents 注解。
    • 字段有 DeclareParents 注解:创建相应的 Advisor
    • 字段无 DeclareParents 注解:跳过该字段。
  • 返回所有 Advisor:将包含所有 Advisor 的列表返回给 Spring 容器。

3. TargetSource 的构建和作用

  在

  1. Spring AOP

中,

  1. TargetSource

是一个重要的概念,它用于描述

  1. AOP

代理的目标对象。目标对象是被代理对象,即实际执行业务逻辑的对象。

  1. TargetSource

定义了如何获取这个目标对象以及它的生命周期管理。

3.1 TargetSource 的作用

  1. 获取目标对象:TargetSource 提供了获取目标对象的机制,代理对象在执行方法调用时,通过 TargetSource 获取实际的目标对象。
  2. 目标对象的生命周期管理:TargetSource 负责目标对象的生命周期管理,比如创建、缓存、销毁等。
  3. 支持多种代理模式:通过不同的 TargetSource 实现,可以支持不同的代理模式,比如单例、多例、懒加载等。

常见的 TargetSource 实现

1. SingletonTargetSource:单例目标源,目标对象是单例的,每次获取的都是同一个实例。

2. PrototypeTargetSource:原型目标源,每次获取目标对象时,都会创建一个新的实例。

3. LazyInitTargetSource:懒加载目标源,目标对象只有在第一次使用时才会被创建。

3.2 TargetSource 的构建

核心类与方法

  • AdvisedSupport:这个类持有 TargetSource 和其他 AOP 配置。
  • ProxyFactory:这个类用于创建 AOP 代理。
  • AopProxy:具体的代理实现类(如 JdkDynamicAopProxyCglibAopProxy)。
  1. AdvisedSupport

  1. Spring AOP

框架中一个核心的配置类,它持有

  1. TargetSource

,以及其他关于

  1. AOP

的配置。下面是一些关键代码片段:

  1. publicclassAdvisedSupportextendsProxyConfigimplementsAdvised{// 持有的 TargetSource 实例privateTargetSource targetSource =EmptyTargetSource.INSTANCE;// 设置 TargetSourcepublicvoidsetTargetSource(TargetSource targetSource){this.targetSource =(targetSource !=null? targetSource :EmptyTargetSource.INSTANCE);}// 获取 TargetSourcepublicTargetSourcegetTargetSource(){returnthis.targetSource;}// 其他相关配置和方法}
  1. ProxyFactory

类用于创建

  1. AOP

代理。它继承自

  1. AdvisedSupport

,所以可以直接访问

  1. TargetSource

相关的类用一张图说明:

在这里插入图片描述

代码提出来分析

  1. /**
  2. * 根据此工厂中的设置创建一个新的代理。
  3. * <p>此方法可以重复调用。如果我们添加或删除接口,效果会有所不同。
  4. * 可以添加和删除拦截器。
  5. * <p>使用默认的类加载器:通常是线程上下文类加载器(如果代理创建需要)。
  6. * @return 代理对象
  7. */publicObjectgetProxy(){// 调用 createAopProxy() 方法创建 AOP 代理,然后调用其 getProxy() 方法返回代理对象returncreateAopProxy().getProxy();}/**
  8. * 创建一个新的 AOP 代理。子类应调用此方法来获取新的 AOP 代理,而不应直接使用 this 作为参数创建 AOP 代理。
  9. * @return 新的 AOP 代理
  10. */protectedfinalsynchronizedAopProxycreateAopProxy(){// 如果代理还没有激活,则激活代理if(!this.active){activate();}// 调用 AopProxyFactory 的 createAopProxy 方法创建新的 AOP 代理returngetAopProxyFactory().createAopProxy(this);}/**
  11. * 根据 AdvisedSupport 配置创建一个 AOP 代理。此方法决定使用哪种代理实现(JDK 动态代理或 CGLIB 代理)。
  12. **/@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);}}
  1. getProxy

方法是调用

  1. createAopProxy()

方法创建

  1. AOP

代理对象,然后调用创建的

  1. AOP

代理对象的

  1. getProxy()

方法返回实际的代理对象。

  1. createAopProxy

方法首先检查代理是否已激活,如果未激活则调用

  1. activate()

方法激活代理。调用

  1. getAopProxyFactory().createAopProxy(this)

方法,通过

  1. AopProxyFactory

创建新的

  1. AOP

代理对象。

  1. createAopProxy

方法过程如下:

  • 检查是否在原生镜像中执行,以及是否需要优化、代理目标类或没有用户提供的代理接口。
  • 获取目标类,如果目标类为空,抛出 AopConfigException 异常。
  • 如果目标类是接口、代理类或 lambda 表达式,使用 JdkDynamicAopProxy 创建代理。
  • 否则,使用 ObjenesisCglibAopProxy 创建代理。
  • 如果不满足上述条件,默认使用 JdkDynamicAopProxy

下面时序图是说明

  1. TargetSource

  1. Spring AOP

中的构建和使用过程:

在这里插入图片描述
关键步骤说明

1. 创建并配置 AdvisedSupport 实例:

  • Spring 容器创建一个 AdvisedSupport 实例,并根据配置设置其属性,包括 TargetSource

2. 设置 TargetSource:

  • AdvisedSupport 实例中设置 TargetSource,它定义了如何获取目标对象。

3. 创建 AopProxy:

  • Spring 容器调用 AopProxyFactory 创建 AopProxy 实例,传递 AdvisedSupport 作为配置。

4. 获取配置信息:

  • AopProxyFactoryAdvisedSupport 获取创建 AopProxy 所需的配置信息。

5. 根据配置创建 AopProxy 实例:

  • AopProxyFactory 根据 AdvisedSupport 配置决定使用哪种 AopProxy 实现(如 JdkDynamicAopProxyCglibAopProxy),并创建 AopProxy 实例。

6. 获取 TargetSource:

  • AopProxyAdvisedSupport 中获取 TargetSource 实例。

7. 获取目标对象:

  • AopProxy 调用 TargetSourcegetTarget 方法获取实际的目标对象实例。

8. 创建代理对象:

  • AopProxy 使用目标对象创建代理对象。

9. 返回代理对象:

  • AopProxy 将创建的代理对象返回给 Spring 容器。

10. 返回代理对象给调用者:

  • Spring 容器将代理对象返回给调用者。

欢迎一键三连~

有问题请留言,大家一起探讨学习

----------------------Talk is cheap, show me the code-----------------------

标签: Java spring aop

本文转载自: https://blog.csdn.net/qq_34115899/article/details/139025496
版权归原作者 砖业洋__ 所有, 如有侵权,请联系我们删除。

“Spring高手之路22——AOP切面类的封装与解析”的评论:

还没有评论