深入明白Spring AOP中的@EnableAspectJAutoProxy

打印 上一主题 下一主题

主题 891|帖子 891|积分 2675

本文分享自华为云社区《Spring高手之路20——深入明白@EnableAspectJAutoProxy的气力》,作者: 砖业洋__。
1. 初始调试代码

面向切面编程(AOP)是一种编程范式,用于增强软件模块化,通过将横切关注点(如事件管理、安全等)分离出业务逻辑。Spring AOP是Spring框架中实现AOP的一种方式,它通过署理机制在运行时向对象动态地添加增强。AspectJ是一种更强大的AOP实现,它通过编译时和加载时织入,提供了比Spring AOP更丰富的增强选项。本文将探索如何通过Spring AOP举行简单的AOP配置和实现。
后续源码分析就用这个前置关照的代码调试
  1. package com.example.demo.aspect;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Component
  8. public class MyAspect {
  9.     @Before("execution(* com.example.demo.service.MyService.performAction(..))")
  10.     public void beforeAdvice(JoinPoint joinPoint) {
  11.         System.out.println("Before method: " + joinPoint.getSignature().getName());
  12.     }
  13. }
复制代码
  1. package com.example.demo.configuration;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  4. @Configuration
  5. @EnableAspectJAutoProxy
  6. public class AppConfig {
  7. }
复制代码
  1. package com.example.demo.service;
  2. import org.springframework.stereotype.Service;
  3. // 一个简单的服务类
  4. @Service
  5. public class MyService {
  6.     public void performAction() {
  7.         System.out.println("Performing an action");
  8.     }
  9. }
复制代码
  1. package com.example.demo;
  2. import com.example.demo.service.MyService;
  3. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  4. import org.springframework.context.annotation.ComponentScan;
  5. //主应用类
  6. @ComponentScan(basePackages = "com.example.demo")
  7. public class DemoApplication {
  8.     public static void main(String[] args) {
  9.         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class);
  10.         MyService myService = context.getBean(MyService.class);
  11.         myService.performAction();  // 调用方法,触发AOP增强
  12.     }
  13. }
复制代码
2. 源码跟踪分析

2.1 初探@EnableAspectJAutoProxy

上面代码中,AppConfig配置类里有个@EnableAspectJAutoProxy注解,前面说过,@EnableAspectJAutoProxy注解告诉Spring框架去寻找带有@Aspect注解的类,Spring AOP通过读取@EnableAspectJAutoProxy注解的属性来配置署理的行为。
下面用时序图来展示通过@EnableAspectJAutoProxy注解启用面向切面编程(AOP)的过程。
解读:
1、启动ApplicationContext:
应用 (App) 向 ApplicationContext 发送消息以启动Spring的应用上下文。这是Spring应用的初始化阶段,负责设置Spring的核心功能,包括Bean的加载和管理。
2、加载配置类:
ApplicationContext 接着加载 配置类 (ConfigClass)。这个配置类包含了应用的配置信息,如Bean定义和AOP支持的相关注解等。
3、检测@EnableAspectJAutoProxy:
配置类完成加载后,检查是否包含 @EnableAspectJAutoProxy 注解。此注解是启用Spring AOP署理的关键,它指示Spring框架自动为符合条件的Bean创建AOP署理。
4、注册AspectJAutoProxyCreator:
一旦检测到@EnableAspectJAutoProxy注解,ApplicationContext 会注册 AspectJAutoProxyCreator (APC)。这个组件是一个BeanPostProcessor,它在Spring容器的bean初始化阶段介入,自动检测容器中所有带有@Aspect注解的类,并为这些类创建署理。这个署理创建过程不仅包括实现关照逻辑的织入,还涉及对被署理对象的调用举行拦截,确保在执行目标方法前后能够执行相应的关照(advice)。
5、扫描和注册Beans:
ApplicationContext 继承扫描应用中的其他 Bean,并将它们注册到Spring容器中。这包括普通的Bean和那些可能成为AOP署理目标的Bean。
6、辨认@Aspect注解:
在Bean的扫描过程中,辨认出带有 @Aspect 注解的Bean(AspectBean)。这些Bean定义了AOP的切面,如关照方法(advice),指定在某些方法执行前后或抛出非常时执行。
7、请求创建署理:
当辨认到@Aspect注解的Bean时,这些Bean会向 AspectJAutoProxyCreator 发出请求,要求创建相应的署理。
8、调用创建署理:
AspectJAutoProxyCreator 收到创建署理的请求后,调用署理工厂 (ProxyFactory) 来构建具体的署理实例。
9、构建署理Bean:
署理工厂 根据AspectJAutoProxyCreator的指示,为@Aspect注解的Bean创建署理。这些署理将封装原Bean,并在调用原Bean的方法时,按照@Aspect定义执行相应的前置、后置或非常关照。
10、注册署理Bean:
创建完成的署理Bean(ProxyBean)被注册回 ApplicationContext,替换或增加到原有的Bean配置中。
11、完成Bean加载和初始化:
所有Bean,包括新注册的署理Bean,都被加载和初始化后,ApplicationContext 向应用 (App) 发送消息,表示Bean加载和初始化工作已完成,应用可以开始执行。
来看看源码,这里可以看到@Import 导入了一个注册器AspectJAutoProxyRegistrar。
@EnableAspectJAutoProxy注解启用Spring的自动署理机制,该注解有两个重要的属性配置:proxyTargetClass和exposeProxy。proxyTargetClass属性默认为false,此时Spring利用JDK动态署理来署理接口。假如设置为true,则Spring将利用CGLIB来署理类,这在目标对象没有实现接口时特别有效。exposeProxy属性默认为false,假如设置为true,允许通过AopContext类访问当前的署理对象,这在需要在目标对象内部方法调用自身被署理的方法时非常有效。
2.2 registerBeanDefinitions方法和时序图分析

本节源码都基于5.3.16分析。
这段代码重要涉及2.1节时序图中的“加载配置类”和“注册AspectJAutoProxyCreator”这两个步骤。
在AspectJAutoProxyRegistrar类的registerBeanDefinitions方法打上断点调试。
这个方法重要负责根据@EnableAspectJAutoProxy注解的设置来配置Spring AOP的行为,包括是否利用CGLIB举行类署理而不是基于接口的JDK署理,以及是否允许在被署理的对象内部通过AopContext访问署理对象。这两个设置对于控制Spring AOP的行为至关重要,特别是在处理复杂的署理场景和高级AOP功能时。
代码提出来分析:
  1. // 注册Bean定义的方法,通过读取注解元数据和操作Bean定义注册表进行配置
  2. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  3.     // 检查是否已经注册了AspectJ自动代理创建器,如果没有,则进行注册
  4.     AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
  5.    
  6.     // 从导入的类的注解元数据中获取@EnableAspectJAutoProxy注解的属性
  7.     AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
  8.    
  9.     // 检查是否成功获取@EnableAspectJAutoProxy注解的属性
  10.     if (enableAspectJAutoProxy != null) {
  11.         // 检查@EnableAspectJAutoProxy注解的proxyTargetClass属性是否为true
  12.         if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
  13.             // 如果proxyTargetClass为true,则强制AOP代理创建器使用CGLIB来进行类代理
  14.             AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  15.         }
  16.         // 检查@EnableAspectJAutoProxy注解的exposeProxy属性是否为true
  17.         if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
  18.             // 如果exposeProxy为true,则强制AOP代理创建器暴露代理对象,使其能在被代理的对象内部通过AopContext访问
  19.             AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
  20.         }
  21.     }
  22. }
复制代码
这个方法的两个入参阐明一下:

  • importingClassMetadata是AnnotationMetadata范例的实例,它持有关于当前正在被处理的类的注解信息。这里用来检索有关@EnableAspectJAutoProxy注解的信息,这些信息决定了如何配置AOP署理的行为(是否利用CGLIB署理以及是否袒露署理对象)。
  • registry是BeanDefinitionRegistry范例的实例,它是一个用于注册Bean定义的接口。通过这个注册表,可以在运行时向Spring应用上下文添加新的Bean定义或修改现有的Bean定义。这里用于现实调整AOP配置,如注册AOP署理创建器,以及设置署理创建器的行为(根据@EnableAspectJAutoProxy的属性值)。这些操作直接影响了Spring AOP如何在运行时创建和管理AOP署理。
假如流程太抽象,那么用时序图增补
这个时序图展示了Spring AOP配置的完备流程,从检查和注册自动署理创建器,到根据@EnableAspectJAutoProxy注解的设置调整Spring的署理行为。此过程确保了应用的AOP配置能够根据给定的注解属性准确地执行,无论是利用更高性能的CGLIB署理,还是袒露署理以供内部访问。
完备的时序图表明
1. 方法调用开始
调用者 (Caller)触发 registerBeanDefinitions 方法(RBD),这通常发生在应用的配置阶段。
2. 检查并注册自动署理创建器
registerBeanDefinitions 向 AopConfigUtils (AopCU)发起调用,检查是否已注册AspectJ自动署理创建器,或者是否需要注册新的或更新现有的署理创建器。
3. 自动署理创建器的注册和更新

  • AopConfigUtils 向 Registry (Reg)执行现实的注册或更新操作。
  • Registry 完成更新后反馈给 AopConfigUtils。
  • AopConfigUtils 然后将结果返回给 registerBeanDefinitions。
4. 获取@EnableAspectJAutoProxy注解的属性
registerBeanDefinitions 接着从 AnnotationConfigUtils (ACU)获取@EnableAspectJAutoProxy注解的相关属性,这些属性决定署理的行为。
5. 根据属性设置署理方式

  • 假如注解的proxyTargetClass属性为真,意味着需要利用CGLIB来举行类署理而不是基于接口的署理。
  • registerBeanDefinitions 要求 AopConfigUtils 逼迫利用CGLIB署理。
  • AopConfigUtils 更新 Registry 中相关Bean定义的设置以利用CGLIB。
  • Registry 确认设置已更新,然后 AopConfigUtils 关照 registerBeanDefinitions 配置完成。
6. 设置是否袒露署理

  • 假如注解的exposeProxy属性为真,意味着需要袒露署理,允许通过AopContext访问当前署理。
  • registerBeanDefinitions 要求 AopConfigUtils 逼迫袒露署理。
  • AopConfigUtils 在 Registry 中举行相应设置更新。
  • Registry 确认设置已更新,然后 AopConfigUtils 关照 registerBeanDefinitions 配置完成。
7. 配置流程完成
一旦所有设置完成,registerBeanDefinitions 向调用者报告配置流程已完成。
2.3 registerOrEscalateApcAsRequired方法和时序图分析

看到刚刚第一句注册后置处理器,我们来详细看看

这段代码重要与2.1节时序图中的“注册AspectJAutoProxyCreator”步骤相对应。AspectJAutoProxyCreator是由Spring内部管理的一个自动署理创建器,用于基于AspectJ的注解来创建AOP署理。它与用户定义的切面(利用@Aspect注解的类)相区分,后者指定了具体的关照(如@Before, @AfterReturning等)和切点表达式。在Spring的AOP实现中,署理创建器负责现实的署理对象创建工作,而用户定义的切面提供了应用于这些署理对象的关照逻辑。具体而言,它形貌了如何在Spring的ApplicationContext中检查并可能更新或注册一个新的自动署理创建器(AspectJAutoProxyCreator)。
直接分析registerOrEscalateApcAsRequired方法
  1. // 定义一个用于注册或升级自动代理创建器的静态方法
  2. private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
  3.     // 断言,确保传入的registry不为空
  4.     Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
  5.    
  6.     // 检查容器是否已经包含名为"org.springframework.aop.config.internalAutoProxyCreator"的Bean定义
  7.     if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
  8.         // 获取已存在的自动代理创建器的Bean定义
  9.         BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
  10.         
  11.         // 检查当前注册的自动代理创建器类名是否与传入的cls类名不同
  12.         if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
  13.             // 找到当前自动代理创建器的优先级
  14.             int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
  15.             // 找到需要注册的自动代理创建器的优先级
  16.             int requiredPriority = findPriorityForClass(cls);
  17.             
  18.             // 比较两个优先级,若已注册的优先级低,则更新为新的自动代理创建器类
  19.             if (currentPriority < requiredPriority) {
  20.                 apcDefinition.setBeanClassName(cls.getName());
  21.             }
  22.         }
  23.         
  24.         // 若已存在自动代理创建器且不需要升级,则返回null
  25.         return null;
  26.     } else {
  27.         // 若未注册自动代理创建器,则创建一个新的RootBeanDefinition实例
  28.         RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
  29.         
  30.         // 设置bean定义的来源
  31.         beanDefinition.setSource(source);
  32.         
  33.         // 设置bean定义的属性,这里设置"order"属性为最小整数值,表示最高优先级
  34.         beanDefinition.getPropertyValues().add("order", Integer.MIN_VALUE);
  35.         
  36.         // 设置bean定义的角色,通常ROLE_INFRASTRUCTURE表示框架内部使用的组件
  37.         beanDefinition.setRole(2);
  38.         
  39.         // 在注册表中注册名为"org.springframework.aop.config.internalAutoProxyCreator"的新自动代理创建器Bean定义
  40.         registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
  41.         
  42.         // 返回新创建的Bean定义
  43.         return beanDefinition;
  44.     }
  45. }
复制代码
这个方法重要用于控制Spring AOP框架中的自动署理创建器(AutoProxyCreator)的注册与优先级升级,确保AOP功能按预期工作,特别是在有多个自动署理创建器可能存在时确保准确的配置和行为优先级。
自动署理创建器(AutoProxyCreator)是一个核心组件,根据配置(如注解、XML配置或程序的指定)辨认需要增强的Bean,并自动为这些Bean创建署理。这些署理可以在方法调用前后添加额外的行为,而不修改原有代码的基础上,实现如安全检查、事件管理、日志记载等横切关注点。
假如流程太抽象,那么用时序图增补

这个时序图展示了 registerOrEscalateApcAsRequired 方法如何根据已存在的自动署理创建器Bean定义的情况来决定执行的操作。通过检查、比较和可能的更新或创建操作,它确保了最适合的类被用于自动署理创建器。假如当前注册的自动署理创建器足够适合,不会举行更改;假如不适合,会举行更新或创建新的Bean定义,以保证体系配置的最优化。
1. 开始调用
调用者发起对 registerOrEscalateApcAsRequired 方法的调用。该方法接收三个参数:类(cls),注册表(registry)和源信息(source)。
2. 检查Bean定义是否存在
registerOrEscalateApcAsRequired 向 BeanDefinitionRegistry 查询是否已存在名为 “internalAutoProxyCreator” 的Bean定义。
3. 处理已存在的Bean定义

  • 假如 BeanDefinitionRegistry 确认Bean定义已存在(返回true),registerOrEscalateApcAsRequired 从 BeanDefinitionRegistry 请求获取该Bean定义。
  • BeanDefinitionRegistry 将 BeanDefinition 返回给 registerOrEscalateApcAsRequired。
  • registerOrEscalateApcAsRequired 利用返回的 BeanDefinition 检查并比较当前Bean的类与新传入的类 cls 的优先级。
4. 决定是否更新Bean定义

  • 假如新类 cls 的优先级更高,registerOrEscalateApcAsRequired 会在 BeanDefinition 中更新类名为新类 cls.getName()。
  • 更新操作完成后,BeanDefinition 关照 BeanDefinitionRegistry 更新已完成。
  • 假如当前已注册的类的优先级足够高或相同,不需要举行更新,registerOrEscalateApcAsRequired 直接返回null给调用者。
5. 处理不存在的Bean定义

  • 假如 BeanDefinitionRegistry 确认没有找到名为 “internalAutoProxyCreator” 的Bean定义(返回false),registerOrEscalateApcAsRequired 将创建一个新的 BeanDefinition。
  • 新创建的 BeanDefinition 被注册到 BeanDefinitionRegistry。
  • 注册完成后,BeanDefinitionRegistry 确认新的BeanDefinition已注册。
  • registerOrEscalateApcAsRequired 终极将新创建的BeanDefinition返回给调用者。
点击关注,第一时间了解华为云奇怪技术~
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表