Spring源码解析系列(二)@Transactional 源码深度解读与事务传播机制详解

共计 4222 个字符,预计需要花费 11 分钟才能阅读完成。

一、为什么要深入理解 @Transactional

@Transactional 是 Spring 框架中使用最广泛的注解之一,尤其在微服务、分布式系统与业务系统中,它承载了关键数据一致性的责任。

但在实际使用中,我们常常遇到以下困惑:

  • 为什么 @Transactional 加在 private 方法上事务不生效?
  • 调用同类方法为什么事务失效?
  • 多个事务嵌套时,是谁控制了回滚逻辑?
  • REQUIRES_NEW、NESTED 等传播行为是如何实现的?
  • rollbackFor、noRollbackFor 是怎么起作用的?

要彻底搞懂这些问题,必须深入理解其底层实现原理与传播机制的源码细节

二、@Transactional 的核心原理概览

Spring 中的事务机制是通过 AOP(面向切面编程) 实现的。事务的执行流程简要如下:

@Transactional 注解
      ↓
TransactionAttributeSource(注解解析)
      ↓
TransactionInterceptor(拦截器处理)
      ↓
PlatformTransactionManager(事务管理器)
      ↓
数据库连接的事务控制(提交 / 回滚)

核心组件

组件 作用
@Transactional 事务元数据标记
TransactionAttributeSource 读取事务配置
TransactionInterceptor AOP 拦截器,控制事务边界
PlatformTransactionManager 真正执行事务的控制器
TransactionSynchronizationManager 存储事务上下文(线程变量)

三、@Transactional 是如何生效的?

1.注解驱动的入口:@EnableTransactionManagement

我们在 Spring Boot 中通常不显式声明 @EnableTransactionManagement,但它已经由自动配置类引入。它的作用是启用基于代理的事务功能

关键类

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement { ... }

它导入的核心类是:

  • ProxyTransactionManagementConfiguration:配置 AOP 拦截器
  • TransactionInterceptor:拦截方法,处理事务
  • TransactionAttributeSource:从方法上读取事务注解信息

2 AOP 代理创建:事务切面注册过程

@Bean
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(...) {
    return new BeanFactoryTransactionAttributeSourceAdvisor();
}

BeanFactoryTransactionAttributeSourceAdvisor 是一个 AOP Advisor,具备以下能力:

  • 使用 TransactionAttributeSourcePointcut 匹配 @Transactional 方法
  • 配置 TransactionInterceptor 作为 Advice
  • 注册到 Spring 的 AOP 容器中

其执行流程由 Spring AOP 的 AnnotationAwareAspectJAutoProxyCreator 自动创建代理。

四、TransactionInterceptor 源码解析(重点)

1.拦截方法入口

public Object invoke(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    Class<?> targetClass = invocation.getThis().getClass();
    return invokeWithinTransaction(method, targetClass, invocation::proceed);
}

这是 AOP 调用 @Transactional 方法时的核心处理逻辑

2.invokeWithinTransaction 核心处理流程

protected Object invokeWithinTransaction(...) {
    // 1. 获取事务属性(注解)
    TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(...);

    // 2. 获取事务管理器
    PlatformTransactionManager tm = determineTransactionManager(txAttr);

    // 3. 构造事务信息
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, ...);

    Object retVal;
    try {
        // 4. 执行目标方法
        retVal = invocation.proceed();

        // 5. 提交事务
        commitTransactionAfterReturning(txInfo);
    } catch (Throwable ex) {
        // 6. 回滚事务
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // 7. 清理事务上下文
        cleanupTransactionInfo(txInfo);
    }

    return retVal;
}

3.事务传播机制的控制:createTransactionIfNecessary

事务传播行为决定是否要新开一个事务,或者加入已有事务,关键代码如下:

public final TransactionStatus getTransaction(TransactionDefinition definition) {
    Object transaction = doGetTransaction(); // 从线程变量中获取连接或事务上下文

    if (isExistingTransaction(transaction)) {
        // 当前存在事务,根据传播行为判断
        switch (definition.getPropagationBehavior()) {
            case PROPAGATION_REQUIRES_NEW:
                suspend(transaction); // 挂起旧事务
                return startTransaction(definition); // 新建事务
            case PROPAGATION_NESTED:
                return createSavepoint(transaction);
            case PROPAGATION_REQUIRED:
                return participateInTransaction(transaction); // 参与已有事务
            ...
        }
    } else {
        // 不存在事务,根据传播行为判断是否新建
        if (definition.getPropagationBehavior() == PROPAGATION_REQUIRED
         || definition.getPropagationBehavior() == PROPAGATION_REQUIRES_NEW) {
            return startTransaction(definition);
        }
        // SUPPORTS / NOT_SUPPORTED 等则不创建事务
    }
}

五、事务传播机制详解

Spring 提供了 7 种事务传播行为:

传播行为 说明 场景示例
REQUIRED 默认行为,有就加入,没有就新建 大多数业务场景
REQUIRES_NEW 挂起当前事务,新建事务 日志记录等不可与主事务共命运
NESTED 嵌套事务,使用 savepoint 局部回滚
SUPPORTS 有事务就参与,无事务就非事务执行 非关键业务
NOT_SUPPORTED 无事务执行,挂起当前事务 查询场景
NEVER 必须无事务,否则抛异常 限制性强
MANDATORY 必须存在事务,否则抛异常 强依赖主事务的子任务

六、异常回滚策略(rollbackFor 的实现机制)

默认情况下,Spring 只会在出现 未检查异常(RuntimeException)或 Error 时才会回滚事务。

可以通过如下方式控制回滚策略:

@Transactional(rollbackFor = Exception.class)

源码逻辑:

if (txAttr.rollbackOn(ex)) {
    txManager.rollback(status);
} else {
    txManager.commit(status);
}

rollbackOn方法:

public boolean rollbackOn(Throwable ex) {
    if (rollbackForClassNames.contains(ex.getClass().getName())) return true;
    if (ex instanceof RuntimeException || ex instanceof Error) return true;
    return false;
}

七、失效场景剖析与解决

场景 说明 解决方案
方法是 private Spring AOP 基于代理,无法拦截 private 方法 使用 public
自调用 同类方法内部调用不会走代理 拆分到独立 Bean 中
异步调用 @Async 与事务线程分离 改为同步或传递上下文
配置错误 忘记开启事务或未注入 PlatformTransactionManager 添加注解或配置
非运行时异常 默认不回滚 指定 rollbackFor

八、总结与最佳实践

推荐用法:

  • 默认使用 REQUIRED,特殊场景使用 REQUIRES_NEW
  • 不要在构造函数或 @PostConstruct 中开启事务
  • 分离事务控制逻辑,避免大事务控制所有流程
  • 明确异常控制规则,显式声明 rollbackFor
  • 避免事务方法之间的直接互相调用(自调用失效)

九、总结

Spring 的事务机制虽然强大,但也有很多坑。理解其原理和源码实现,不仅有助于写出更加健壮、正确的业务代码,也有助于我们在系统设计中合理地拆分职责和控制边界。

十、关注后续专栏

本文是《Spring 源码解析系列》专栏的第 2 篇,后续将发布:

  • 《Spring AOP 原理与 CGLIB / JDK 动态代理源码分析》
  • 《Spring 中的事件监听机制(ApplicationEvent)源码与实战》

正文完
 0
李卷卷
版权声明:本站原创文章,由 李卷卷 于2023-06-30发表,共计4222字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)