Skip to content

切面实例化模型 (Aspect Instantiation Models)

提示

这是一个高级话题。如果你刚开始接触 AOP,可以先跳过这一节,以后再回来阅读。

默认情况下,应用上下文中每个切面都有一个单一的实例。AspectJ 称之为 单例实例化模型 (Singleton instantiation model)。此外,还可以定义具有其他生命周期的切面。Spring 支持 AspectJ 的 perthispertargetpertypewithin 实例化模型;目前不支持 percflowpercflowbelow

你可以通过在 @Aspect 注解中指定 perthis 子句来声明一个 perthis 切面。请看下面的例子:

java
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
public class MyAspect {

	private int someState;

	@Before("execution(* com.xyz..service.*.*(..))")
	public void recordServiceUsage() {
		// ...
	}
}
kotlin
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
class MyAspect {

	private val someState: Int = 0

	@Before("execution(* com.xyz..service.*.*(..))")
	fun recordServiceUsage() {
		// ...
	}
}

在上述示例中,perthis 子句的作用是:为每个执行业务服务的唯一服务对象(切入点表达式匹配的连接点处绑定到 this 的每个唯一对象)创建一个切面实例。切面实例在服务对象上的方法第一次被调用时创建。当服务对象超出作用域时,切面也随之超出作用域。在切面实例创建之前,其中的任何通知都不会运行。一旦切面实例被创建,其中声明的通知就会在匹配的连接点处运行,但仅当服务对象是与该切点相关联的那一个时。有关 per 子句的更多信息,请参阅 AspectJ 编程指南。

pertarget 实例化模型的工作方式与 perthis 完全相同,但它为匹配的连接点处的每个唯一目标对象(Target object)创建一个切面实例。


补充教学

1. 为什么需要非单例切面?

在 95% 的场景下,切面都是无状态的(例如日志记录器、事务管理器),单例模型是最完美的方案,既省内存又高效。 但是,如果你需要切面来保存与特定业务对象相关的状态,单例就不够用了。例如:

  • 各 Bean 独立的调用计数器:如果你不想用 Map 在单例切面里存计数逻辑,可以给每个 Bean 分配一个切面实例。
  • 特定上下文的缓存:为特定的 Service 实例维护一套私有缓存。

2. perthis vs. pertarget:Spring AOP 中的细微差别

由于 Spring AOP 是基于代理的,这里存在一个“多重身份”:

  • this:代表当前的代理对象(Proxy)
  • target:代表原始的目标对象(Target)

在 Spring AOP 中使用 perthis 时,Spring 会为每一个被代理的 Proxy 实例 创建一个切面实例。考虑到通常一个目标对象对应一个代理对象,两者在实际效果上往往非常接近,但逻辑语义不同。

3. 生命周期与作用域

非单例切面的生命周期是寄生型的:

  • 创建时机:它不是在容器启动时创建的,而是在目标方法第一次被调用触发 AOP 逻辑时延迟初始化的。
  • 销毁时机:它的生命周期与它所绑定的对象实例同步。如果目标 Bean 被垃圾回收了,对应的切面实例也会被回收。

4. 性能与内存警告 ⚠️

使用这些模型时要非常谨慎:

  • 内存开销:如果你的系统中有 10 万个匹配的 Bean,使用 perthis 就会产生 10 万个切面实例。而在默认的单例模型下,只有 1 个。
  • 管理成本:Spring 必须在内部维护这些实例的映射关系,这会带来额外的系统开销。

5. 与 Spring Bean Scope 的区别

不要把 perthis 与 Spring 的 @Scope("prototype") 混淆:

  • Prototype:每次 getBean() 都会创建一个新实例,切面本身还是尝试去拦截。
  • perthis:控制的是切面逻辑与目标实例的绑定关系。即使切面本身定义为单例,只要标注了 perthis,它在逻辑执行层面就不再是单例的了。

Based on Spring Framework.