Spring事务失效的原因分析

https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java

https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-tx/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java

根据以上两个源码

AbstractPlatformTransactionManager.java 和 TransactionSynchronizationManager.java

总结Spring事务失效的原因:

类别    根本原因    典型场景
线程问题    事务上下文绑定在 ThreadLocal,但业务操作不在同一线程    多线程/异步操作
代理问题    @Transactional 方法未被 AOP 拦截    self-invocation、非 public 方法
传播行为    getTransaction 根据传播策略决定是否创建事务    不匹配的 REQUIRED/REQUIRES_NEW/SUPPORTS
底层资源    数据源自动提交、事务管理器未配置正确    autoCommit=true,DataSource 与 TM 不匹配
异常策略    Spring 默认只对 RuntimeException 回滚    捕获或抛出 checked exception

核心归因:Spring 事务依赖 ThreadLocal + 代理拦截,只要这两者失效,事务就失效。
其他因素(传播、异常、数据源)都是“间接原因”。

好了好了,根据以上的源码分析和 Spring 事务管理机制,可以总结出 Spring 事务失效的根本原因,核心其实都和 事务上下文无法正确绑定到当前线程 有关。下面详细分析:


1️⃣ Spring 事务核心机制回顾

Spring 的事务管理主要依赖:

  1. AbstractPlatformTransactionManager:事务模板,负责完整流程控制(begin → commit/rollback)。

    • getTransaction():判断是否已有事务、处理传播行为、启动新事务。

    • commit():检查回滚标记、触发回调、执行平台特定提交。

    • rollback():事务回滚。

  2. TransactionSynchronizationManager

    • 核心是 ThreadLocal 存储事务相关资源(比如 JDBC Connection)。

    • bindResource() / unbindResource():将事务资源绑定到当前线程。

    • isActualTransactionActive():判断当前线程是否有活动事务。

  3. AOP / @Transactional

    • 使用 TransactionInterceptor 拦截业务方法。

    • 拦截器内部调用 PlatformTransactionManager.getTransaction()

    • 方法执行完成后调用 commit()rollback()


2️⃣ 事务失效的典型根本原因

(1) 事务资源没有绑定到线程

  • Spring 使用 ThreadLocal 管理事务资源。

  • 如果业务逻辑没有在同一个线程中执行,ThreadLocal 就取不到事务上下文。

  • 典型场景:

    1. 多线程调用:在 @Transactional 方法中开启新线程执行数据库操作。

    2. 异步调用:使用 @AsyncExecutorService 执行数据库操作。

  • 结果:新的线程中 TransactionSynchronizationManager.isActualTransactionActive() 返回 false,事务失效。


(2) 事务方法没有被 AOP 代理拦截

  • Spring 的 @Transactional 是 基于代理的,默认:

    • JDK 动态代理:只能拦截接口方法。

    • CGLIB 代理:拦截类方法。

  • 失效场景

    1. 同类内部调用(self-invocation):

      
      

      class Service { @Transactional void methodA() { ... } void methodB() { methodA(); // self-invocation, @Transactional 不会生效 } }

    2. 非 public 方法(默认 Spring 事务只对 public 方法生效)。


(3) 事务传播行为不匹配

  • 如果传播行为设置不当,也可能导致事务失效:

    • 比如方法使用 REQUIRES_NEW,但调用方没有事务。

    • 如果方法使用 SUPPORTS,但调用方没有事务,内部就不会开启新事务。

  • 本质:AbstractPlatformTransactionManager.getTransaction() 根据传播行为决定是否创建新事务,传播策略不匹配就不会开启事务。


(4) 数据源或事务管理器配置问题

  • 事务只是 逻辑封装,必须依赖正确的底层资源:

    1. 使用的 DataSource 是非 XA、自动提交被开启 (autoCommit=true)。

    2. PlatformTransactionManager 未正确注入到代理或业务层。

  • 这种情况下,即使 getTransaction() 被调用,底层数据库也可能在每条 SQL 自动提交,导致“事务失效”。


(5) 异常处理不当

  • 默认 Spring 事务只对 RuntimeException / Error 回滚。

  • 如果捕获异常后吞掉,或者抛出 检查型异常(checked exception):

    • commit() 会认为事务正常提交。

    • 结果业务回滚预期不生效。


3️⃣ 总结根本原因

类别根本原因典型场景
线程问题事务上下文绑定在 ThreadLocal,但业务操作不在同一线程多线程/异步操作
代理问题@Transactional 方法未被 AOP 拦截self-invocation、非 public 方法
传播行为getTransaction 根据传播策略决定是否创建事务不匹配的 REQUIRED/REQUIRES_NEW/SUPPORTS
底层资源数据源自动提交、事务管理器未配置正确autoCommit=true,DataSource 与 TM 不匹配
异常策略Spring 默认只对 RuntimeException 回滚捕获或抛出 checked exception

核心归因:Spring 事务依赖 ThreadLocal + 代理拦截,只要这两者失效,事务就失效。其他因素(传播、异常、数据源)都是“间接原因”。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值