一、Spring事务 核心定位与本质
1.1 Spring事务是什么?
Spring 事务是 Spring 框架提供的一站式事务管理解决方案 ,核心是对 JDBC事务、JTA事务 进行统一封装、抽象和增强,为开发者提供一致的事务操作API,彻底解决原生JDBC事务代码冗余、耦合度高的问题。
- 核心价值:解耦业务逻辑与事务控制逻辑,开发者无需手动编写开启/提交/回滚事务的代码,专注业务开发。
- 本质:Spring事务不是实现了新的事务机制,而是对数据库原生事务的封装 ,最终事务的提交和回滚,依然是由数据库本身的事务机制(如MySQL的InnoDB事务)来完成的,如果数据库本身不支持事物,那么Spring事务也无效。
1.2 Spring事务的两大核心类型
Spring提供两种事务使用方式,声明式事务是主流,开发首选,两种方式共存,按需选择:
✅ 方式1:声明式事务(99%生产场景使用)
- 核心实现:基于 注解
@Transactional或 XML 配置实现,无侵入式,无需修改业务代码; - 核心原理:底层基于AOP动态代理 实现事务的增强,是Spring事务的核心;
- 优点:代码简洁、无侵入、配置灵活、维护成本低;
- 核心注解:
@Transactional(方法/类级别均可标注)。
✅ 方式2:编程式事务(极少数场景使用)
- 核心实现:通过Spring提供的API手动编写事务逻辑,如
TransactionTemplate、PlatformTransactionManager; - 核心特点:侵入式,需要在业务代码中嵌入事务开启、提交、回滚的逻辑;
- 优点:粒度更细,可实现复杂的事务控制逻辑;
- 适用场景:业务逻辑中需要手动控制事务边界的特殊场景(如部分代码需要事务,部分不需要)。
核心重点:日常开发的核心都是 声明式事务 +
@Transactional注解
二、Spring事务 核心底层原理
2.1 核心根基:Spring事务 完全基于 AOP 实现
Spring声明式事务的底层核心 = 动态代理(AOP) + 事务拦截器 ,这是Spring事务的唯一核心原理,所有事务的执行逻辑都围绕这个根基展开。
- 核心逻辑:Spring会为被
@Transactional标注的Bean 创建一个动态代理对象,这个代理对象就是事务的增强器; - 执行链路:所有对目标Bean的方法调用,都会先经过代理对象 ,再由代理对象完成事务的「开启→执行业务→提交/回滚」全流程。
2.2 动态代理的两种实现方式(事务生效的前提)
Spring事务的动态代理和Spring AOP的代理规则完全一致,自动选择代理方式,无感知适配,两种方式决定了事务能否生效:
✅ ① JDK 动态代理(默认优先)
- 生效条件:目标类实现了至少一个接口;
- 实现原理:基于接口生成代理类,代理类和目标类是「兄弟关系」,实现同一个接口;
- 特点:轻量级、无侵入,Spring默认首选。
✅ ② CGLIB 动态代理(兜底方案)
- 生效条件:目标类没有实现任何接口;
- 实现原理:基于目标类生成子类代理对象,通过继承的方式增强目标类;
- 特点:无需接口,兼容性更强,代理类是目标类的子类。
核心注意:两种代理方式对开发者完全透明,Spring自动适配,无需手动配置。
2.3 Spring事务的完整执行链路
Spring事务的本质是 AOP的环绕通知(Around Advice) ,对被@Transactional标注的方法进行全流程拦截,整个执行链路固定不变 ,是理解事务的核心,所有事务的生效/失效都和这个链路有关,共8步核心流程,一环扣一环:
- 外部调用目标对象的事务方法 (如
saveUser()),实际调用的是 Spring创建的动态代理对象; - 代理对象的事务拦截器(核心组件)先执行,开启事务 :通过事务管理器创建数据库连接,关闭连接的自动提交(
conn.setAutoCommit(false)),这是事务的核心; - 事务开启成功后,调用目标对象的真实业务方法,执行业务逻辑(如插入/更新数据库);
- 执行业务方法时,判断是否抛出异常 :
- ✅ 情况一:业务方法正常执行,无异常抛出 → 执行事务提交 逻辑,数据库执行
commit(),事务完成; - ❌ 情况二:业务方法抛出指定异常 → 执行事务回滚 逻辑,数据库执行
rollback(),数据恢复到事务执行前的状态;
- ✅ 情况一:业务方法正常执行,无异常抛出 → 执行事务提交 逻辑,数据库执行
- 无论提交/回滚,最后都会释放数据库连接,将连接归还到连接池;
- 若事务执行过程中出现系统异常(如断电),数据库会自动回滚事务,保证数据一致性;
- 若事务执行成功,代理对象将业务结果返回给调用方;
- 若事务执行失败,代理对象将异常抛出,由调用方处理。
核心总结:Spring事务的执行逻辑 = 代理拦截 → 开启事务 → 执行业务 → 提交/回滚 → 释放资源,所有逻辑都是Spring自动完成,开发者无需关心。
三、Spring事务 三大核心组件(架构基石,源码核心)
Spring事务的所有功能,都是基于三个核心接口实现的,这三个接口是Spring事务的架构基石,也是源码的核心,三者分工明确、解耦彻底,共同支撑起整个事务体系
3.1 核心接口1:PlatformTransactionManager - 事务管理器
事务管理器是Spring事务的「总指挥」 ,是事务操作的统一入口 ,所有事务的开启、提交、回滚操作,都由该接口的实现类完成,定义了事务的核心操作规范。
核心方法(3个核心方法)
java
public interface PlatformTransactionManager {
// 1. 创建事务,返回事务状态对象(包含事务的所有信息)
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 2. 提交事务:只有事务状态为正常时,才会执行提交
void commit(TransactionStatus status) throws TransactionException;
// 3. 回滚事务:事务执行失败时,执行回滚
void rollback(TransactionStatus status) throws TransactionException;
}
常用实现类(按需适配不同场景)
DataSourceTransactionManager:最常用,适配JDBC/MyBatis/Mybatis-Plus的单数据源事务,基于JDBC事务实现;JdbcTransactionManager:专门适配原生JDBC的事务管理器;JtaTransactionManager:适配分布式事务(多数据源/跨服务),基于JTA实现;HibernateTransactionManager:适配Hibernate框架的事务管理器。
3.2 核心接口2:TransactionDefinition - 事务定义(事务规则配置)
事务定义是事务的「规则手册」 ,定义了事务的所有配置规则 ,比如事务的传播行为、隔离级别、超时时间、是否只读等,这些规则决定了事务的执行行为。
该接口的配置规则,就是我们在@Transactional注解中配置的属性,核心配置项如下:
- 事务传播行为:7种,核心配置,决定多个事务方法嵌套调用时,事务的传播方式;
- 事务隔离级别:4种+默认,决定事务之间的隔离程度,解决脏读、不可重复读、幻读问题;
- 事务超时时间:默认-1(永不超时),超过指定时间事务自动回滚,避免长事务占用资源;
- 是否只读:默认false,设置为true时,事务只能执行查询操作,不能执行增删改,数据库会做性能优化;
- 回滚规则:决定哪些异常触发回滚,哪些异常不触发回滚。
3.3 核心接口3:TransactionStatus - 事务状态(事务运行时信息)
事务状态是事务的「运行时快照」 ,封装了事务运行过程中的所有状态信息 ,是事务管理器执行提交/回滚的依据,相当于事务的「身份证」。
核心作用:
- 记录事务的当前状态(是否开启、是否提交、是否回滚、是否有保存点);
- 提供事务的操作方法(如设置回滚、判断是否完成);
- 事务管理器通过该对象,判断事务的最终行为(提交/回滚)。
3.4 三大组件的核心关系
TransactionDefinition(规则) → PlatformTransactionManager(管理器) → TransactionStatus(状态)
- 开发者配置
TransactionDefinition的规则(注解/XML); - 事务管理器根据规则,创建事务并返回事务状态;
- 事务管理器根据事务状态,执行提交/回滚操作。
四、Spring事务 核心属性详解 @Transactional
@Transactional是Spring声明式事务的核心注解,所有事务规则都通过该注解配置,可标注在类上 或方法上 ,优先级:方法级别 > 类级别(方法上的配置会覆盖类上的配置)。
4.1 核心属性1:propagation - 事务传播行为
核心定义
事务传播行为 :指的是多个被@Transactional标注的事务方法嵌套调用时,外层方法的事务如何传递到内层方法,内层方法的事务如何影响外层方法的事务。
核心理解:事务传播行为是Spring独有的特性,数据库原生事务没有这个概念,是Spring对事务的增强。
7种传播行为
Spring通过枚举Propagation定义了7种传播行为,所有行为都是基于数据库事务实现,无任何自研逻辑:
-
Propagation.REQUIRED(默认值,最常用)
- 核心规则:如果当前存在事务,则加入该事务;如果当前没有事务,则新建一个事务。
- 通俗理解:有则复用,无则新建,99%的业务场景都用这个,比如下单→扣库存→减余额的嵌套调用,全部在同一个事务中,要么一起成功,要么一起失败。
-
Propagation.REQUIRES_NEW(独立事务,常用)
- 核心规则:无论当前是否存在事务,都新建一个独立的事务,新事务和原有事务相互独立,互不影响。
- 通俗理解:强制新建事务,比如下单成功后,记录日志的操作,日志的事务独立于下单事务,即使日志保存失败,也不会影响下单的提交。
-
Propagation.NESTED(嵌套事务,常用)
- 核心规则:如果当前存在事务,则在当前事务中创建一个嵌套事务(保存点);如果当前没有事务,则新建一个事务。
- 通俗理解:内层事务是外层事务的子集,内层事务回滚,不会影响外层事务;外层事务回滚,内层事务必然回滚。适用于:主业务+子业务,子业务失败可单独回滚,主业务成功则提交。
-
其他传播行为(了解即可)
SUPPORTS:有事务则加入,无事务则以非事务方式执行;NOT_SUPPORTED:以非事务方式执行,有事务则挂起当前事务;MANDATORY:必须在事务中执行,无事务则抛出异常;NEVER:必须以非事务方式执行,有事务则抛出异常。
4.2 核心属性2:isolation - 事务隔离级别
核心定义
事务隔离级别 :是数据库的原生特性 ,指的是多个并发事务之间的隔离程度 ,解决并发事务带来的 脏读、不可重复读、幻读 三大问题。
Spring通过枚举Isolation对数据库的隔离级别进行了封装,完全和数据库的隔离级别一一对应,无任何修改。
前置知识:并发事务的3个问题
- 脏读 :一个事务读取到另一个事务未提交的数据,若另一个事务回滚,读取的数据是无效的;
- 不可重复读 :一个事务中,多次读取同一数据,结果不一致(其他事务修改并提交了该数据);
- 幻读 :一个事务中,多次执行同一查询,结果集的条数不一致(其他事务新增/删除并提交了数据)。
4种隔离级别 + 1个默认
Spring支持的4种隔离级别,和MySQL的InnoDB引擎完全一致,隔离级别越高,数据一致性越好,并发性能越低:
- Isolation.DEFAULT(默认值) :使用数据库默认的隔离级别 ,MySQL默认是
REPEATABLE_READ(可重复读),Oracle默认是READ_COMMITTED(读已提交); - Isolation.READ_UNCOMMITTED(读未提交) :最低级别,允许读取未提交的数据,会出现脏读、不可重复读、幻读;
- Isolation.READ_COMMITTED(读已提交) :允许读取已提交的数据,解决脏读 ,会出现不可重复读、幻读;
- Isolation.REPEATABLE_READ(可重复读) :MySQL默认,同一事务内多次读取数据一致,解决脏读、不可重复读 ,会出现幻读;
- Isolation.SERIALIZABLE(串行化):最高级别,事务串行执行,无并发,解决所有问题,性能极差,几乎不用。
事务隔离级别配置的「优先级排序」
Spring + 数据库 事务隔离级别配置的完整优先级排序(从高到低),优先级高的会直接覆盖优先级低的: 方法上的@Transactional(isolation=xxx) > 类上的@Transactional(isolation=xxx) > Spring全局事务配置(xml/java配置类的默认隔离级别)> 数据库的全局默认隔离级别
核心实现原理:当数据库事物是的隔离级别是可重复读,但是通过注解将隔离级别设置为读已提交时,Spring 会通过 JDBC 执行SET SESSION指令,临时 修改当前数据库连接的隔离级别,事务结束后重置;
4.3 核心属性3:其他配置
- timeout:事务超时时间,单位:秒,默认-1(永不超时)。超过指定时间,事务自动回滚,避免长事务占用数据库连接;
- readOnly:是否只读,默认false。设置为true时,事务只能执行查询操作,不能执行增删改,数据库会做查询优化,提升性能;
- rollbackFor :指定触发事务回滚的异常类型,可配置多个异常,核心属性!
- noRollbackFor :指定不触发事务回滚的异常类型,可配置多个异常;
- rollbackForClassName:和rollbackFor一致,只是配置的是异常的全类名字符串;
- noRollbackForClassName:和noRollbackFor一致,配置异常全类名。
4.4 核心重点:事务的回滚规则(90%的人踩坑点)
这是开发中最容易导致事务失效的核心点 ,Spring事务的回滚规则是默认规则:
✅ Spring事务默认回滚规则
- 事务只会对 运行时异常(RuntimeException) 和 错误(Error) 进行回滚;
- 事务不会对 受检异常(Checked Exception) 进行回滚(如
IOException、SQLException); - 通俗理解:Spring认为,运行时异常是程序逻辑错误 ,需要回滚;受检异常是业务预期异常,不需要回滚。
✅ 解决方案(必做)
在@Transactional注解中,通过rollbackFor手动指定需要回滚的异常,生产中必须配置,避免事务不回滚:
java
// 示例:指定所有异常都回滚,最常用的配置方式
@Transactional(rollbackFor = Exception.class)
五、Spring事务 核心源码解析
Spring事务的源码核心在spring-tx包下,我们只看最核心的执行流程和核心类,无需关注细枝末节。
5.1 源码核心前置说明
- Spring事务的核心实现是 AOP的事务拦截器,所有事务逻辑都在拦截器中完成;
- 核心注解
@Transactional的解析,由Spring的后置处理器完成; - 所有核心逻辑都是基于「三大核心组件」实现,无任何自研逻辑。
5.2 核心类1:TransactionInterceptor - 事务拦截器
这是Spring事务的核心执行类 ,也是AOP的环绕通知实现类,所有被@Transactional标注的方法,都会被该类拦截,事务的开启、提交、回滚全流程都在这个类中实现 。
核心方法:invoke(MethodInvocation invocation) → 事务的核心执行入口,逻辑和我们之前讲的8步执行链路完全一致,核心伪代码如下:
java
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取当前方法的事务配置规则(TransactionDefinition)
TransactionAttribute txAttr = getTransactionAttribute(invocation.getMethod());
// 2. 获取事务管理器(PlatformTransactionManager)
PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 3. 获取目标方法名,用于日志打印
String joinpointIdentification = methodIdentification(invocation.getMethod());
// 4. 核心:开启事务,获取事务状态
TransactionStatus status = tm.getTransaction(txAttr);
Object retVal;
try {
// 5. 执行目标对象的真实业务方法
retVal = invocation.proceed();
} catch (Throwable ex) {
// 6. 捕获异常:根据回滚规则,执行事务回滚
completeTransactionAfterThrowing(tm, status, ex);
throw ex;
} finally {
// 7. 清理事务上下文
cleanupTransactionInfo(status);
}
// 8. 业务执行成功,提交事务
tm.commit(status);
// 9. 返回业务结果
return retVal;
}
5.3 核心类2:AnnotationTransactionAttributeSource
负责解析@Transactional注解 ,将注解中的配置(传播行为、隔离级别、超时时间等)解析为TransactionAttribute对象(事务规则),是注解式事务的核心解析类。
5.4 核心类3:TransactionAspectSupport
事务拦截器的父类,封装了事务的通用逻辑,如事务状态的管理、异常回滚规则的判断、事务的提交/回滚等,是事务的基础支撑类。
5.5 源码核心总结
Spring事务的源码逻辑 = 注解解析 → 事务管理器获取 → 事务开启 → 业务执行 → 异常回滚/正常提交,所有逻辑都围绕三大核心组件展开,简洁清晰,无任何炫技设计。
六、Spring事务 常见失效场景
Spring事务失效的场景全部都是有规律可循 的,所有失效场景的本质:事务的动态代理没有生效 或 事务的回滚规则不匹配。
6.1 失效场景1:事务方法被定义为 private 私有方法
- ❌ 失效原因:Spring的动态代理只能代理公共方法(public),private方法无法被代理,事务拦截器无法拦截,事务自然失效;
- ✅ 解决方案:将事务方法的修饰符改为 public(必须)。
6.2 失效场景2:同一个类中,事务方法内部调用
- ❌ 失效原因:如
A类的methodA()调用A类的methodB(),两个方法都加了@Transactional,此时是目标对象的内部调用,没有经过Spring的动态代理对象,事务拦截器无法拦截,methodB的事务失效; - ✅ 解决方案:
- 方案一:将两个方法拆分到不同的类中,通过Spring注入调用,走代理对象;
- 方案二:在当前类中,手动注入自己,通过注入的对象调用方法(推荐);
- 方案三:通过AopContext获取当前代理对象,调用方法。
6.3 失效场景3:异常被 try-catch 捕获,没有抛出
- ❌ 失效原因:Spring事务的回滚,是通过捕获方法抛出的异常来触发的,如果业务方法中用try-catch捕获了异常,并且没有手动抛出,Spring无法感知异常,事务不会回滚;
- ✅ 解决方案:
- 方案一:捕获异常后,手动抛出异常 (如
throw new RuntimeException(e)); - 方案二:捕获异常后,手动调用事务回滚 (
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly())。
- 方案一:捕获异常后,手动抛出异常 (如
6.4 失效场景4:没有配置 rollbackFor,抛出受检异常
- ❌ 失效原因:Spring默认只对运行时异常和Error回滚,若业务方法抛出受检异常(如IOException),且未配置
rollbackFor,事务不会回滚; - ✅ 解决方案:在
@Transactional注解中,强制配置rollbackFor = Exception.class,指定所有异常都回滚。
6.5 失效场景5:目标类没有被Spring容器管理
- ❌ 失效原因:事务方法所在的类,没有被
@Component/@Service/@Controller等注解标注,没有被Spring扫描并创建Bean,自然没有动态代理,事务失效; - ✅ 解决方案:给目标类添加Spring的组件注解,确保被Spring容器管理。
6.6 失效场景6:事务传播行为配置错误
- ❌ 失效原因:配置了不支持事务的传播行为,如
NOT_SUPPORTED(非事务执行)、NEVER(禁止事务),导致事务失效; - ✅ 解决方案:使用默认的
REQUIRED或常用的REQUIRES_NEW/NESTED,避免使用不支持事务的传播行为。
6.7 失效场景7:数据源没有配置事务管理器
- ❌ 失效原因:Spring的事务管理器需要手动配置(SpringBoot自动配置除外),如果数据源没有绑定事务管理器,事务无法生效;
- ✅ 解决方案:配置
DataSourceTransactionManager,并绑定数据源。
6.8 失效场景8:多线程调用事务方法(特殊高频)
- ❌ 失效原因:Spring的事务是基于线程绑定的,事务上下文存储在当前线程中,多线程调用时,子线程无法获取父线程的事务上下文,事务失效;
- ✅ 解决方案:避免在事务方法中开启新线程执行数据库操作,或使用分布式事务解决。
核心总结:所有事务失效的场景,都可以归结为一句话:Spring的事务代理没有拦截到目标方法,或者异常没有被Spring感知到。
七、Spring事务 核心总结
✅ Spring事务核心原理一句话总结
Spring声明式事务基于 AOP动态代理 实现,通过事务拦截器对目标方法进行环绕增强,自动完成「开启事务→执行业务→提交/回滚」的全流程,本质是对数据库原生事务的封装和解耦。
✅ @Transactional注解核心一句话总结
@Transactional的核心配置是传播行为、隔离级别、回滚规则 ,生产中必须配置rollbackFor = Exception.class,避免受检异常导致事务不回滚,方法必须是public。
✅ 事务失效核心一句话总结
事务失效的本质是动态代理未生效 (如private方法、内部调用)或异常未被感知(如try-catch捕获、未配置回滚规则),所有失效场景都有对应的解决方案。
✅ 生产最佳实践
- 所有需要事务的方法,都使用
@Transactional(rollbackFor = Exception.class),这是万能配置; - 事务方法尽量是public,避免内部调用;
- 异常处理时,要么抛出异常,要么手动回滚事务;
- 尽量缩短事务的执行时间,避免长事务占用数据库连接;
- 读多写少的场景,设置
readOnly = true,提升性能。