Skip to content

配合 AspectJ 使用 @Transactional (Using @Transactional with AspectJ)

你也可以通过 AspectJ 切面,在 Spring 容器之外使用 Spring 框架的 @Transactional 支持。为此,首先使用 @Transactional 注解标注你的类(以及可选的类方法),然后将你的应用程序与 spring-aspects.jar 文件中定义的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 进行链接(织入)。你还必须为该切面配置一个事务管理器。你可以使用 Spring 框架的 IoC 容器来处理切面的依赖注入。配置事务管理切面最简单的方法是使用 <tx:annotation-driven/> 元素并将 mode 属性指定为 aspectj,如使用 @Transactional 中所述。因为这里我们要关注的是在 Spring 容器之外运行的应用程序,所以我们将展示如何以编程方式实现这一点。

注意

在继续之前,你可能需要分别阅读使用 @TransactionalAOP

以下示例显示了如何创建事务管理器并配置 AnnotationTransactionAspect 来使用它:

java
// 构建一个合适的事务管理器
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());

// 配置 AnnotationTransactionAspect 以使用它;这必须在执行任何事务方法之前完成
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
kotlin
// 构建一个合适的事务管理器
val txManager = DataSourceTransactionManager(getDataSource())

// 配置 AnnotationTransactionAspect 以使用它;这必须在执行任何事务方法之前完成
AnnotationTransactionAspect.aspectOf().transactionManager = txManager

注意

当你使用此切面时,你必须注解实现类(或该类中的方法,或两者兼而有之),而不是该类实现的接口(如果有)。AspectJ 遵循 Java 的规则,即接口上的注解不会被继承。

类上的 @Transactional 注解指定了该类中任何公共 (public) 方法执行的默认事务语义。

类内部方法上的 @Transactional 注解会覆盖类注解给出的默认事务语义(如果存在)。你可以注解任何方法,无论可见性如何。

要使用 AnnotationTransactionAspect 织入你的应用程序,你要么必须使用 AspectJ 构建你的应用程序(参见 AspectJ 开发人员指南),要么使用加载时织入。有关使用 AspectJ 进行加载时织入的讨论,请参阅 Spring 框架中使用 AspectJ 进行加载时织入


补充教学

1. 为什么需要 AspectJ 模式?

默认的 Spring AOP 是基于动态代理 (Proxy) 的,它有一些天然的局限性:

  • 自调用失效:在同一个类中,方法 A 调用方法 B,方法 B 的事务注解会失效,因为绕过了代理。
  • 非 Bean 对象:如果你的对象不是 Spring 容器管理的 Bean(例如通过 new 出来的领域对象 Domain Entity),Spring AOP 根本无法拦截它。
  • 私有方法:动态代理(无论是 JDK 还是 CGLIB)通常只能拦截 public/protected 方法。

AspectJ 模式通过修改字节码(Weaving)来工作,直接把事务逻辑“缝”进你的代码里,因此它能完美解决上述所有问题。

2. 实战场景:领域驱动设计 (DDD)

AspectJ 在 DDD 中非常有用。在充血模型(Rich Domain Model)中,业务逻辑经常放在实体(Entity)中,而不是 Service 中。

java
// 这是一个普通的 Java 对象,不是 Spring Bean
public class Order {
    
    @Transactional // 使用 AspectJ,即使是 new 出来的对象,这个注解也能生效!
    public void pay() {
        // ... 业务逻辑
        this.save();
    }
}

通过配置 AspectJ 织入,即使是你手动 new Order(),在调用 pay() 时,Spring 的事务切面也能神奇地介入并开启事务。这是标准 Spring AOP 做不到的。

3. "aspectOf()" 方法哪里来的?

在上面的代码中,你看到了 AnnotationTransactionAspect.aspectOf()。你翻遍源码可能也找不到这个方法。 这是 AspectJ 的魔法。AspectJ 编译器(ajc)在编译时会自动为每个切面类生成静态的 aspectOf() 方法,用于获取当前运行时的切面单例实例。这是配置 AspectJ 切面的标准方式。

Based on Spring Framework.