深入理解Spring AOP中的@EnableAspectJAutoProxy
本文分享自华为云社区《
Spring高手之路20——深入理解@EnableAspectJAutoProxy的力量
》,作者: 砖业洋__。
1. 初始调试代码
面向切面编程(
AOP
)是一种编程范式,用于增强软件模块化,通过将横切关注点(如事务管理、安全等)分离出业务逻辑。
Spring AOP
是
Spring
框架中实现
AOP
的一种方式,它通过代理机制在运行时向对象动态地添加增强。
AspectJ
是一种更强大的
AOP
实现,它通过编译时和加载时织入,提供了比
Spring AOP
更丰富的增强选项。本文将探索如何通过
Spring AOP
进行简单的
AOP
配置和实现。
后续源码分析就用这个前置通知的代码调试
package com.example.demo.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Componentpublic classMyAspect {
@Before("execution(* com.example.demo.service.MyService.performAction(..))")public voidbeforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method:" +joinPoint.getSignature().getName());
}
}
package com.example.demo.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxypublic classAppConfig {
}
package com.example.demo.service;
import org.springframework.stereotype.Service;//一个简单的服务类 @Servicepublic classMyService {public voidperformAction() {
System.out.println("Performing an action");
}
}
package com.example.demo;
import com.example.demo.service.MyService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;//主应用类 @ComponentScan(basePackages = "com.example.demo")public classDemoApplication {public static voidmain(String[] args) {
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(DemoApplication.class);
MyService myService= context.getBean(MyService.class);
myService.performAction();//调用方法,触发AOP增强 }
}
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
功能时。
代码提出来分析:
//注册Bean定义的方法,通过读取注解元数据和操作Bean定义注册表进行配置 public voidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//检查是否已经注册了AspectJ自动代理创建器,如果没有,则进行注册 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);//从导入的类的注解元数据中获取@EnableAspectJAutoProxy注解的属性 AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);//检查是否成功获取@EnableAspectJAutoProxy注解的属性 if (enableAspectJAutoProxy != null) {//检查@EnableAspectJAutoProxy注解的proxyTargetClass属性是否为true if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {//如果proxyTargetClass为true,则强制AOP代理创建器使用CGLIB来进行类代理 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}//检查@EnableAspectJAutoProxy注解的exposeProxy属性是否为true if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {//如果exposeProxy为true,则强制AOP代理创建器暴露代理对象,使其能在被代理的对象内部通过AopContext访问 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
这个方法的两个入参说明一下:
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
方法
//定义一个用于注册或升级自动代理创建器的静态方法 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?>cls, BeanDefinitionRegistry registry, @Nullable Object source) {//断言,确保传入的registry不为空 Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//检查容器是否已经包含名为"org.springframework.aop.config.internalAutoProxyCreator"的Bean定义 if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {//获取已存在的自动代理创建器的Bean定义 BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");//检查当前注册的自动代理创建器类名是否与传入的cls类名不同 if (!cls.getName().equals(apcDefinition.getBeanClassName())) {//找到当前自动代理创建器的优先级 int currentPriority =findPriorityForClass(apcDefinition.getBeanClassName());//找到需要注册的自动代理创建器的优先级 int requiredPriority =findPriorityForClass(cls);//比较两个优先级,若已注册的优先级低,则更新为新的自动代理创建器类 if (currentPriority <requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}//若已存在自动代理创建器且不需要升级,则返回null return null;
}else{//若未注册自动代理创建器,则创建一个新的RootBeanDefinition实例 RootBeanDefinition beanDefinition = newRootBeanDefinition(cls);//设置bean定义的来源 beanDefinition.setSource(source);//设置bean定义的属性,这里设置"order"属性为最小整数值,表示最高优先级 beanDefinition.getPropertyValues().add("order", Integer.MIN_VALUE);//设置bean定义的角色,通常ROLE_INFRASTRUCTURE表示框架内部使用的组件 beanDefinition.setRole(2);//在注册表中注册名为"org.springframework.aop.config.internalAutoProxyCreator"的新自动代理创建器Bean定义 registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);//返回新创建的Bean定义 returnbeanDefinition;
}
}
这个方法主要用于控制
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
返回给调用者。