📌 PDF:大白话说Java面试题 — 06_Spring篇
第17题:讲讲Spring 事务的实现方式、原理,以及隔离级别
📚 回答:
- 核心考点: Spring 事务是后端开发的核心能力,大厂面试不会只问"@Transactional 怎么用",而是深入考察 声明式事务的 AOP 代理链路(
TransactionInterceptor如何拦截、TransactionAspectSupport如何管理事务状态)、事务传播行为的 7 种场景与边界情况(REQUIRED/REQUIRES_NEW/NESTED的嵌套与回滚差异)、事务失效的 8 种典型场景及排查方法、以及 Spring 事务与数据库事务的映射关系(IsolationLevel如何转换为数据库隔离级别)。面试官真正想判断的是:你是否能从源码层面理解事务的完整执行链路,以及能否在生产中快速定位和解决事务不生效、事务回滚失败、嵌套事务异常等高频问题。
1. Spring 事务的两种实现方式
-
1.1 编程式事务
通过
TransactionTemplate或PlatformTransactionManager手动控制事务边界:// 方式一:TransactionTemplate(推荐) @Autowired private TransactionTemplate transactionTemplate; public void transferMoney(final Long fromId, final Long toId, final BigDecimal amount) { transactionTemplate.execute(status -> { accountDao.debit(fromId, amount); accountDao.credit(toId, amount); return null; // 无返回值 }); } // 方式二:PlatformTransactionManager(更底层) @Autowired private PlatformTransactionManager transactionManager; public void transferMoney(Long fromId, Long toId, BigDecimal amount) { TransactionDefinition definition = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(definition); try { accountDao.debit(fromId, amount); accountDao.credit(toId, amount); transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } }维度 TransactionTemplate PlatformTransactionManager 代码简洁度 高(回调函数) 低(手动 commit/rollback) 异常处理 自动回滚,抛出 TransactionException手动回滚 返回值 支持泛型返回值 无返回值 事务配置 通过属性设置 通过 TransactionDefinition适用场景 少量需要编程式事务的场景 需要精细控制事务边界 -
1.2 声明式事务
通过
@Transactional注解声明事务,由 Spring AOP 自动管理:@Service public class AccountService { @Transactional( propagation = Propagation.REQUIRED, // 传播行为 isolation = Isolation.DEFAULT, // 隔离级别 readOnly = false, // 是否只读 timeout = 30, // 超时时间(秒) rollbackFor = Exception.class // 回滚异常类型 ) public void transferMoney(Long fromId, Long toId, BigDecimal amount) { accountDao.debit(fromId, amount); accountDao.credit(toId, amount); } }@Transactional 的属性详解:
属性 说明 默认值 常用值 propagation事务传播行为 REQUIREDREQUIRED/REQUIRES_NEW/NESTEDisolation事务隔离级别 DEFAULTREAD_COMMITTED/REPEATABLE_READreadOnly是否只读事务 falsetrue(查询场景优化)timeout超时时间(秒) -1(无限制)30rollbackFor触发回滚的异常 RuntimeExceptionException.classnoRollbackFor不回滚的异常 无 BusinessException.class
2. 声明式事务的底层原理——AOP 代理链路
-
2.1 事务代理的创建
@Transactional的事务管理基于 Spring AOP 实现。Spring 通过AbstractAutoProxyCreator扫描@Transactional注解,为目标 Bean 创建代理对象。// 事务代理创建的关键类 public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator { // 扫描 @Transactional 注解,创建 TransactionInterceptor 增强 } -
2.2 TransactionInterceptor 的拦截逻辑
TransactionInterceptor是事务管理的核心,继承自TransactionAspectSupport:public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } }事务执行的完整链路:
调用 @Transactional 方法 ↓ CGLIB/JDK 代理拦截 ↓ TransactionInterceptor.invoke() ↓ TransactionAspectSupport.invokeWithinTransaction() ↓ 【1】获取事务属性(@Transactional 配置) ↓ 【2】获取 PlatformTransactionManager(如 DataSourceTransactionManager) ↓ 【3】判断当前是否存在事务 ↓ ├── 无事务 → 根据传播行为决定是否创建新事务 └── 有事务 → 根据传播行为决定挂起/加入/嵌套 ↓ 【4】开启事务(doBegin) ├── 从 DataSource 获取 Connection ├── 设置 autoCommit = false ├── 设置隔离级别(setTransactionIsolation) └── 将 Connection 绑定到 ThreadLocal(TransactionSynchronizationManager) ↓ 【5】执行业务方法(invocation.proceed()) ↓ 【6】判断执行结果 ├── 正常完成 → commitTransactionAfterReturning() │ ├── 触发 beforeCommit 同步回调 │ ├── 执行数据库 commit() │ └── 触发 afterCommit 同步回调 └── 抛出异常 → completeTransactionAfterThrowing() ├── 判断异常类型是否匹配 rollbackFor ├── 匹配 → 执行 rollback() └── 不匹配 → 执行 commit() ↓ 【7】清理事务状态(cleanupTransactionInfo) ├── 恢复挂起的事务(如果有) └── 解绑 ThreadLocal 中的 Connection -
2.3 事务与数据库连接的绑定
Spring 通过
TransactionSynchronizationManager将数据库连接绑定到当前线程:public abstract class TransactionSynchronizationManager { private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); // 绑定 Connection 到当前线程 public static void bindResource(Object key, Object value) { Map<Object, Object> map = resources.get(); if (map == null) { map = new HashMap<>(); resources.set(map); } map.put(key, value); // key = DataSource, value = ConnectionHolder } }这意味着:同一线程内,嵌套事务共享同一个数据库连接(除非传播行为是
REQUIRES_NEW)。
3. 事务传播行为——7 种场景深度解析
Spring 定义了 7 种事务传播行为,是事务管理中最容易混淆的部分:
| 传播行为 | 含义 | 当前无事务 | 当前有事务 | 典型场景 |
|---|---|---|---|---|
REQUIRED | 必须有事务 | 创建新事务 | 加入当前事务 | 默认,最常用 |
SUPPORTS | 支持事务 | 非事务执行 | 加入当前事务 | 查询方法,不强制事务 |
MANDATORY | 强制要求事务 | 抛出异常 | 加入当前事务 | 强制上层必须开启事务 |
REQUIRES_NEW | 必须新事务 | 创建新事务 | 挂起当前事务,创建新事务 | 独立事务,失败不影响外层 |
NOT_SUPPORTED | 不支持事务 | 非事务执行 | 挂起当前事务,非事务执行 | 不需要事务的方法 |
NEVER | 禁止事务 | 非事务执行 | 抛出异常 | 绝对不能在事务中执行 |
NESTED | 嵌套事务 | 创建新事务 | 创建 savepoint 嵌套事务 | 子事务回滚不影响主事务 |
-
3.1 REQUIRED——默认且最常用
@Transactional public void methodA() { methodB(); // REQUIRED,加入 A 的事务 } @Transactional(propagation = Propagation.REQUIRED) public void methodB() { // 与 methodA 共享同一个事务 }特点:B 加入 A 的事务,B 异常回滚会导致 A 也回滚(同一个事务)。
-
3.2 REQUIRES_NEW——独立事务
@Transactional public void methodA() { methodB(); // REQUIRES_NEW,挂起 A 的事务,创建新事务 // B 的失败不会影响 A } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // 独立事务,有自己的 Connection }特点:A 和 B 是完全独立的事务。B 提交/回滚不影响 A。但注意:如果 B 抛异常且未被捕获,异常会传播到 A,导致 A 也回滚。
正确用法:
@Transactional public void methodA() { try { methodB(); // B 失败不影响 A } catch (Exception e) { log.error("B failed", e); // 捕获异常,阻止传播 } } -
3.3 NESTED——嵌套 Savepoint
@Transactional public void methodA() { // 插入记录 1 methodB(); // NESTED,创建 savepoint // 如果 B 回滚,只回滚到 savepoint,记录 1 仍然保留 } @Transactional(propagation = Propagation.NESTED) public void methodB() { // 插入记录 2 // 如果失败,只回滚记录 2,记录 1 不受影响 }特点:基于数据库 savepoint 实现,子事务回滚不影响主事务。但注意:外层事务回滚会导致所有嵌套事务回滚。且不是所有数据库都支持 savepoint(如 MySQL 支持,某些旧版本 Oracle 不支持)。
-
3.4 传播行为对比总结
场景:methodA(外层)调用 methodB(内层) REQUIRED + REQUIRED:A 和 B 同一个事务,B 异常 → A 回滚 REQUIRED + REQUIRES_NEW:A 和 B 独立事务,B 异常 → A 不受影响(需捕获异常) REQUIRED + NESTED:B 是 A 的 savepoint,B 回滚 → A 部分回滚到 savepoint
4. 事务隔离级别
Spring 的隔离级别与数据库一致,通过 TransactionDefinition 定义:
| Spring 隔离级别 | 数据库对应 | 脏读 | 不可重复读 | 幻读 | 适用场景 |
|---|---|---|---|---|---|
DEFAULT | 数据库默认 | - | - | - | 通用,推荐 |
READ_UNCOMMITTED | 读未提交 | ❌ | ❌ | ❌ | 几乎不用 |
READ_COMMITTED | 读已提交 | ✅ | ❌ | ❌ | 互联网高并发 |
REPEATABLE_READ | 可重复读 | ✅ | ✅ | ⚠️ | MySQL 默认,金融对账 |
SERIALIZABLE | 串行化 | ✅ | ✅ | ✅ | 极端一致性,性能差 |
Spring 隔离级别设置:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void queryData() { }
注意:Spring 的隔离级别最终通过 JDBC Connection.setTransactionIsolation() 设置,实际效果取决于数据库支持程度。
5. 事务失效的 8 种场景——生产避坑指南
-
5.1 自调用导致事务失效
@Service public class UserService { @Transactional public void methodA() { methodB(); // ❌ 自调用!不走代理,事务不生效! } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // 期望新事务,但实际不会生效 } }原因:同类方法内部调用走的是
this引用,而非 Spring 代理对象。
解决:注入自身代理、使用AopContext.currentProxy()、或重构拆分。 -
5.2 方法不是 public
@Transactional只能作用于 public 方法,private/protected/package-private 方法事务不生效。 -
5.3 异常被捕获
@Transactional public void method() { try { dao.insert(data); } catch (Exception e) { log.error("Error", e); // ❌ 吞掉异常!事务不会回滚! } }解决:捕获后重新抛出,或配置
rollbackFor = Exception.class。 -
5.4 异常类型不匹配
@Transactional默认只回滚RuntimeException,非受检异常(IOException、SQLException)不回滚。@Transactional(rollbackFor = Exception.class) // 指定所有异常都回滚 public void method() throws IOException { } -
5.5 数据库引擎不支持事务
MySQL 的 MyISAM 引擎不支持事务,必须使用 InnoDB。
-
5.6 多线程环境
事务上下文绑定在 ThreadLocal 中,子线程无法继承父线程事务。
@Transactional public void method() { new Thread(() -> { dao.insert(data); // ❌ 不在父线程事务中! }).start(); } -
5.7 事务传播行为配置错误
NOT_SUPPORTED、NEVER会挂起或禁止事务,导致预期外行为。 -
5.8 代理未创建
Bean 未被 Spring 管理,或方法为 final/private,无法创建代理。
6. 面试官追问与高分回答模板
-
追问 1:“Spring 事务的实现方式有哪些?”
低分回答:“有编程式事务和声明式事务,声明式用 @Transactional。”(没有对比和原理)
高分回答:
"Spring 提供两种事务实现方式:
编程式事务:通过
TransactionTemplate或PlatformTransactionManager手动控制事务边界。TransactionTemplate基于回调函数,代码简洁,自动处理回滚;PlatformTransactionManager更底层,需要手动 commit/rollback。适用于需要精细控制事务边界的场景。声明式事务:通过
@Transactional注解声明,由 Spring AOP 自动管理。底层通过TransactionInterceptor拦截方法调用,在invokeWithinTransaction()中完成:获取事务属性 → 判断当前事务状态 → 开启事务(doBegin)→ 执行业务方法 → 根据结果 commit/rollback → 清理事务状态。生产环境推荐声明式事务,代码侵入性低,与业务逻辑解耦。"
-
追问 2:“@Transactional 的底层原理是什么?”
高分回答:
"
@Transactional基于 Spring AOP 实现,核心链路:- 代理创建:
InfrastructureAdvisorAutoProxyCreator扫描@Transactional注解,通过TransactionInterceptor创建代理对象; - 方法拦截:调用
@Transactional方法时,TransactionInterceptor.invoke()拦截; - 事务管理:
TransactionAspectSupport.invokeWithinTransaction()执行:- 获取事务属性(传播行为、隔离级别、超时等);
- 获取
PlatformTransactionManager(如DataSourceTransactionManager); - 根据传播行为决定创建新事务或加入现有事务;
doBegin():从 DataSource 获取 Connection,设置autoCommit=false,设置隔离级别;- 将 Connection 绑定到
TransactionSynchronizationManager(ThreadLocal); - 执行业务方法;
- 正常完成则 commit,异常则根据
rollbackFor判断回滚或提交; cleanupTransactionInfo():恢复挂起的事务,解绑 ThreadLocal。
关键是数据库连接与线程绑定,同一线程内嵌套事务共享同一个 Connection(除非
REQUIRES_NEW)。" - 代理创建:
-
追问 3:“Spring 事务的 7 种传播行为有什么区别?”
高分回答:
"| 传播行为 | 无事务时 | 有事务时 | 典型场景 |
|----------|----------|----------|----------|
|REQUIRED| 创建新事务 | 加入当前事务 | 默认,最常用 |
|SUPPORTS| 非事务执行 | 加入当前事务 | 查询,不强制事务 |
|MANDATORY| 抛异常 | 加入当前事务 | 强制上层开事务 |
|REQUIRES_NEW| 创建新事务 | 挂起当前,创建新事务 | 独立事务,如日志记录 |
|NOT_SUPPORTED| 非事务执行 | 挂起当前,非事务执行 | 不需要事务的方法 |
|NEVER| 非事务执行 | 抛异常 | 禁止在事务中执行 |
|NESTED| 创建新事务 | 创建 savepoint | 子事务回滚不影响主事务 |最常用的是
REQUIRED(默认)和REQUIRES_NEW。REQUIRES_NEW会挂起当前事务、创建新事务,两个事务完全独立。但注意:如果内层抛异常且未被外层捕获,异常会传播到外层,导致外层也回滚。NESTED基于数据库 savepoint 实现,子事务回滚只回滚到 savepoint,但外层回滚会导致所有嵌套事务回滚。" -
追问 4:“事务失效有哪些场景?怎么排查?”
高分回答:
"事务失效有 8 种典型场景:
- 自调用:同类方法内部调用走
this而非代理,事务不生效。解决:注入自身代理、用AopContext.currentProxy()、重构拆分; - 非 public 方法:
@Transactional只能作用于 public 方法; - 异常被捕获:
try-catch吞掉异常,事务感知不到。解决:捕获后重新抛出,或配置rollbackFor; - 异常类型不匹配:默认只回滚
RuntimeException,非受检异常不回滚。解决:rollbackFor = Exception.class; - 数据库引擎不支持:MySQL MyISAM 不支持事务,必须用 InnoDB;
- 多线程:事务上下文绑定 ThreadLocal,子线程无法继承。解决:异步操作后单独处理;
- 传播行为错误:
NOT_SUPPORTED/NEVER挂起或禁止事务; - 代理未创建:Bean 未被 Spring 管理,或方法为 final/private。
排查步骤:
- 确认代理是否创建(
AopUtils.isAopProxy()); - 检查方法是否为 public;
- 检查异常传播路径(是否被捕获、类型是否匹配);
- 检查传播行为配置;
- 确认数据库引擎和隔离级别。"
- 自调用:同类方法内部调用走
-
追问 5:“REQUIRED 和 REQUIRES_NEW 有什么区别?什么场景用 REQUIRES_NEW?”
高分回答:
"| 维度 | REQUIRED | REQUIRES_NEW |
|------|----------|-------------|
| 事务关系 | 加入当前事务 | 完全独立的新事务 |
| Connection | 共享同一个 | 独立的 Connection |
| 回滚影响 | 内层回滚导致外层回滚 | 互不影响 |
| 挂起行为 | 不挂起 | 挂起当前事务 |
| 性能 | 好(无连接切换) | 差(需要新连接) |REQUIRES_NEW的典型场景:- 日志记录:业务失败但日志必须保存;
- 消息发送:业务回滚但消息已发送(需补偿);
- 审计记录:操作记录必须持久化,不受业务事务影响;
- 独立统计:统计操作失败不影响主业务。
注意:内层抛异常必须被外层捕获,否则异常传播会导致外层也回滚。"
-
追问 6:“Spring 事务隔离级别和数据库隔离级别有什么关系?”
高分回答:
"Spring 的隔离级别是对数据库隔离级别的封装和映射:
Spring 隔离级别 数据库行为 说明 DEFAULT使用数据库默认 推荐,不强制指定 READ_UNCOMMITTED读未提交 几乎不用 READ_COMMITTED读已提交 互联网高并发推荐 REPEATABLE_READ可重复读 MySQL 默认 SERIALIZABLE串行化 性能极差 Spring 通过
TransactionDefinition.getIsolationLevel()获取配置,在doBegin()中调用Connection.setTransactionIsolation()设置。实际效果取决于数据库支持程度。现代互联网应用推荐:
- 读操作:
READ_COMMITTED+ 乐观锁(版本号/CAS); - 写操作:
REPEATABLE_READ(MySQL 默认)+ 悲观锁(SELECT ... FOR UPDATE); - 金融对账:
REPEATABLE_READ或SERIALIZABLE。"
- 读操作:
7. 方案选型速查表
| 业务场景 | 推荐方案 | 传播行为 | 隔离级别 | 说明 |
|---|---|---|---|---|
| 普通 CRUD | 声明式 @Transactional | REQUIRED | DEFAULT | 默认配置,简单有效 |
| 查询操作 | @Transactional(readOnly = true) | SUPPORTS | READ_COMMITTED | 只读优化,不强制事务 |
| 转账/扣款 | 声明式 + 编程式混合 | REQUIRED | REPEATABLE_READ | 强一致性,需防幻读 |
| 日志/审计 | @Transactional | REQUIRES_NEW | DEFAULT | 独立事务,失败也要记录 |
| 批量处理 | 编程式 TransactionTemplate | - | READ_COMMITTED | 精细控制,分批提交 |
| 嵌套业务 | @Transactional | NESTED | DEFAULT | 子失败不影响主事务 |
| 高并发读 | @Transactional(readOnly = true) | SUPPORTS | READ_COMMITTED | 不加锁,提升并发 |
| 定时任务 | @Transactional | REQUIRED | READ_COMMITTED | 异常回滚,下次重试 |
💡 面试官想要的满分总结:
Spring 事务的核心是声明式事务 + AOP 代理,通过
TransactionInterceptor拦截方法调用,在TransactionAspectSupport中完成事务的完整生命周期管理。理解事务必须抓住三个关键点:
- 传播行为决定事务边界:
REQUIRED加入当前事务(最常用),REQUIRES_NEW创建独立事务(日志/审计场景),NESTED基于 savepoint 实现嵌套(子失败不影响主)。选择传播行为时,必须考虑异常传播和连接切换的性能开销。- 事务失效有 8 种典型场景:自调用、非 public、异常被捕获、异常类型不匹配、数据库引擎不支持、多线程、传播行为错误、代理未创建。排查时遵循"代理 → 方法权限 → 异常路径 → 传播行为 → 数据库"的顺序。
- 隔离级别是数据库能力的映射:Spring 通过
Connection.setTransactionIsolation()设置,实际效果取决于数据库。现代应用推荐READ_COMMITTED+ 乐观锁处理并发,而非盲目提升隔离级别牺牲性能。生产中最致命的坑是自调用导致事务失效和REQUIRES_NEW 异常未捕获导致外层回滚。优秀的开发者不仅会用
@Transactional,更能从事务传播、隔离级别、异常处理三个维度设计可靠的事务策略。
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

被折叠的 条评论
为什么被折叠?



