@Aspect
注解是 Spring AOP (Aspect-Oriented Programming) 的核心注解之一,用于声明一个切面类。Spring 容器在启动时会解析和处理 @Aspect
注解,并将其转换为相应的代理对象,以实现 AOP 功能。
解析和处理流程:
-
容器启动: Spring 容器启动时,会扫描并加载 Bean 定义。
-
发现
@Aspect
注解:- 如果在 XML 配置中启用了
<aop:aspectj-autoproxy/>
,或者在 Java 配置类上使用了@EnableAspectJAutoProxy
注解,Spring 容器会启用 AspectJ 自动代理。 - 启用自动代理后,容器会创建一个名为
AnnotationAwareAspectJAutoProxyCreator
的BeanPostProcessor
。 AnnotationAwareAspectJAutoProxyCreator
会在 Bean 初始化阶段检查 Bean 是否带有@Aspect
注解。
- 如果在 XML 配置中启用了
-
解析
@Aspect
注解:- 如果发现 Bean 带有
@Aspect
注解,AnnotationAwareAspectJAutoProxyCreator
会解析该注解及其内部的切点 (Pointcut)、通知 (Advice) 等信息。 - 切点 (Pointcut): 使用
@Pointcut
注解定义,指定了哪些连接点 (Join Point) 会被拦截。连接点通常是方法的执行。 - 通知 (Advice): 使用
@Before
、@After
、@AfterReturning
、@AfterThrowing
、@Around
等注解定义,指定了在切点匹配的连接点上执行的操作。@Before
: 在连接点之前执行。@After
: 在连接点之后执行(无论是否发生异常)。@AfterReturning
: 在连接点成功返回后执行。@AfterThrowing
: 在连接点抛出异常后执行。@Around
: 环绕通知,在连接点前后都可以执行,还可以控制目标方法的执行。
AnnotationAwareAspectJAutoProxyCreator
会将解析出的切点和通知信息封装成Advisor
对象。Advisor
代表一个切面,包含了切点和通知。
- 如果发现 Bean 带有
-
创建代理对象:
- 对于需要被 AOP 增强的 Bean(即目标对象,Target Object),
AnnotationAwareAspectJAutoProxyCreator
会为其创建代理对象。 - Spring AOP 使用两种代理方式:
- JDK 动态代理: 如果目标对象实现了接口,Spring 会使用 JDK 动态代理。代理对象实现了与目标对象相同的接口。
- CGLIB 代理: 如果目标对象没有实现接口,Spring 会使用 CGLIB 代理。代理对象是目标对象的子类。
- 代理对象拦截了对目标对象方法的调用,并根据
Advisor
中的切点和通知信息,在方法执行前后执行相应的操作。
- 对于需要被 AOP 增强的 Bean(即目标对象,Target Object),
-
将代理对象放入容器:
AnnotationAwareAspectJAutoProxyCreator
会将创建的代理对象放入 Spring 容器中,替换掉原来的目标对象。- 当其他 Bean 依赖于目标对象时,Spring 容器会注入代理对象,而不是原始的目标对象。
详细说明:
AnnotationAwareAspectJAutoProxyCreator
: 这是 Spring AOP 的核心组件,它实现了BeanPostProcessor
接口,负责解析@Aspect
注解、创建代理对象等。Advisor
: 代表一个切面,包含了切点和通知。Pointcut
: 切点,定义了哪些连接点会被拦截。使用 AspectJ 的切点表达式语言 (Pointcut Expression Language) 来描述。Advice
: 通知,定义了在切点匹配的连接点上执行的操作。Join Point
: 连接点,程序执行过程中的一个点,例如方法的执行、异常的抛出等。Target Object
: 目标对象,需要被 AOP 增强的 Bean。Proxy Object
: 代理对象,Spring AOP 创建的代理对象,拦截了对目标对象方法的调用。
示例:
java">import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 定义切点:拦截所有 com.example.service 包下的所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知:在方法执行前记录日志
@Before("serviceMethods()")
public void beforeAdvice() {
System.out.println("Before method execution...");
}
// 后置通知:在方法执行后记录日志
@After("serviceMethods()")
public void afterAdvice() {
System.out.println("After method execution...");
}
}
在这个例子中:
@Aspect
注解声明了LoggingAspect
类是一个切面。@Pointcut
注解定义了一个名为serviceMethods
的切点,它匹配com.example.service
包下所有类的所有方法。@Before
和@After
注解定义了两个通知,分别在方法执行前后记录日志。
当 Spring 容器启动时,会解析 @Aspect
注解,并为 com.example.service
包下的所有 Bean 创建代理对象。当这些 Bean 的方法被调用时,代理对象会拦截方法调用,并在方法执行前后执行 beforeAdvice
和 afterAdvice
方法。
总结流程图:
+---------------------+ +--------------------------------+ +---------------------+
| Spring 容器 |---->| AnnotationAwareAspectJAutoProxyCreator |---->| 目标对象 |
+---------------------+ +--------------------------------+ +---------------------+
| |
| 1. 发现 @Aspect 注解 |
| 2. 解析 @Aspect、@Pointcut、@Advice |
| 3. 创建 Advisor 对象 |
| |
|----------------------------------------|
| |
| 4. 创建代理对象 (JDK 或 CGLIB) |
| |
V V
+----------------+ +-----------------+
| 代理对象 | | 调用目标方法 |
+----------------+ +-----------------+
| |
+---------------------------+---------------------------+ |
| | | |
| @Before (执行前) | 目标方法执行 | @After (执行后) |
| | | |
+---------------------------+---------------------------+ |
|
<-----------------------------------+