Skip to content

声明切面 (Declaring an Aspect)

在启用了 @AspectJ 支持的情况下,Spring 会自动探测应用上下文中定义的任何 Bean,如果其对应的类是一个 @AspectJ 切面(即标注了 @Aspect 注解),Spring 就会利用它来配置 Spring AOP。

以下两个示例展示了声明一个切面所需的最少步骤。

第一个示例展示了应用上下文中的一个普通 Bean 定义,它指向一个标注了 @Aspect 的 Bean 类:

java
public class ApplicationConfiguration {

	@Bean
	public NotVeryUsefulAspect myAspect() {
		NotVeryUsefulAspect myAspect = new NotVeryUsefulAspect();
		// 在此配置切面的属性
		return myAspect;
	}
}
kotlin
class ApplicationConfiguration {

	@Bean
	fun myAspect() = NotVeryUsefulAspect().apply {
		// 在此配置切面的属性
	}
}
xml
<bean id="myAspect" class="org.springframework.docs.core.aop.ataspectj.aopataspectj.NotVeryUsefulAspect">
	<!-- 在此处配置切面的属性 -->
</bean>

第二个示例展示了 NotVeryUsefulAspect 类的定义,该类使用了 @Aspect 注解:

java
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {
}
kotlin
import org.aspectj.lang.annotation.Aspect

@Aspect
class NotVeryUsefulAspect

切面(标注了 @Aspect 的类)可以像任何其他类一样拥有方法和字段。它们还可以包含切入点(Pointcut)、通知(Advice)和引入(Introduction/Inter-type)的声明。

通过组件扫描自动探测切面

你可以通过多种方式注册切面类:

  • 在 Spring XML 配置中注册为常规 <bean>
  • @Configuration 类中通过 @Bean 方法注册。
  • 类路径扫描:让 Spring 像探测其他普通 Bean 一样自动探测切面。

注意:仅靠 @Aspect 注解不足以让 Spring 进行自动探测。你必须同时添加 @Component 注解(或者按照 Spring 组件扫描规则添加自定义的构型注解,如 @Service 等)。

切面能被其他切面通知吗?

在 Spring AOP 中,切面本身不能成为其他切面通知的目标。类上的 @Aspect 注解将其标记为一个切面,因此会将其从自动代理机制中排除。


补充教学

1. 切面类的“隐身性”

虽然切面类被注册为 Spring 容器中的一个 Bean,但你通常不会在代码中通过 @Autowired 去注入一个切面类并调用它的方法。切面更多是按照“声明式”的规则在后台静默工作。 如果你发现自己需要频繁手动调用切面类的方法,那可能说明这部分逻辑应该抽象到一个普通的 Service 类中,而不是放在切面里。

2. 为什么需要 @Component

这是新手最容易混淆的一点:

  • @Aspect:告诉 Spring,“这是一个 AOP 规则定义类,请解析它里面的通知和切点”。它属于 AspectJ 的范畴。
  • @Component:告诉 Spring,“请把这个类实例化并放入你的 IoC 容器中”。它属于 Spring IoC 的范畴。 如果一个切面类没有被放入容器,Spring AOP 根本没机会扫描到它,切面自然也就不会生效。

3. 切面的生命周期(单例 vs 原型)

默认情况下,应用上下文中的每一个切面都是一个单例(Singleton)

  • 这符合大多数横切关注点的需求(如:日志记录器、事务管理器)。
  • 如果你在切面类中定义了非线程安全的成员变量,必须非常小心,因为所有并发请求都会共享这个切面实例。
  • 虽然 Spring 支持通过 perthispertarget 改变切面生命周期,但在企业开发中极少用到。

4. 切面排他性

官方提到的“切面不能被其他切面通知”,是为了防止 AOP 逻辑陷入死循环。 想象一下:如果切面 A 的逻辑触发了切面 B,切面 B 的逻辑又触发了切面 A,程序就会崩溃。因此,Spring AOP 从设计上就禁止了对切面本身的代理。

Based on Spring Framework.