面向切面编程(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
@Component
public class MyAspect {
@Before("execution(* com.example.demo.service.MyService.performAction(..))")
public void beforeAdvice(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
@EnableAspectJAutoProxy
public class AppConfig {
}
package com.example.demo.service;
import org.springframework.stereotype.Service;
// 一个简单的服务类
@Service
public class MyService {
public void performAction() {
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 class DemoApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class);
MyService myService = context.getBean(MyService.class);
myService.performAction(); // 调用方法,触发AOP增强
}
}
上面代码中,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
类访问当前的代理对象,这在需要在目标对象内部方法调用自身被代理的方法时非常有用。
本节源码都基于5.3.16
分析。
这段代码主要涉及2.1
节时序图中的“加载配置类”和“注册AspectJAutoProxyCreator
”这两个步骤。在AspectJAutoProxyRegistrar
类的registerBeanDefinitions
方法打上断点调试。
这个方法主要负责根据@EnableAspectJAutoProxy
注解的设置来配置Spring AOP
的行为,包括是否使用CGLIB
进行类代理而不是基于接口的JDK
代理,以及是否允许在被代理的对象内部通过AopContext
访问代理对象。这两个设置对于控制Spring AOP
的行为至关重要,特别是在处理复杂的代理场景和高级AOP
功能时。
代码提出来分析:
// 注册Bean定义的方法,通过读取注解元数据和操作Bean定义注册表进行配置
public void registerBeanDefinitions(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.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 = new RootBeanDefinition(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定义
return beanDefinition;
}
}
这个方法主要用于控制Spring AOP
框架中的自动代理创建器(AutoProxyCreator
)的注册与优先级升级,确保AOP
功能按预期工作,特别是在有多个自动代理创建器可能存在时确保正确的配置和行为优先级。
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。