目录
一、Spring事务实现原理
Spring事务管理是Spring框架最核心的功能之一,它通过AOP和ThreadLocal机制,为开发者提供了简洁的事务管理方式。
1.1 核心架构
Spring事务管理采用分层架构设计:
┌─────────────────────────────────────┐
│ TransactionManagement 接口层 │ ← 用户使用
├─────────────────────────────────────┤
│ AbstractPlatformTransactionManager│ ← 抽象实现
├─────────────────────────────────────┤
│ PlatformTransactionManager 实现类 │ ← 具体数据源
│ (DataSource/JPA/JTA/Hibernate) │
└─────────────────────────────────────┘
1.2 核心接口
| 接口 | 作用 |
|---|---|
PlatformTransactionManager |
事务管理器核心接口 |
TransactionDefinition |
定义事务属性(传播、隔离、超时) |
TransactionStatus |
事务运行状态 |
1.3 实现原理:AOP + ThreadLocal
AOP动态代理
当我们使用 @Transactional 注解时,Spring会通过AOP在方法前后添加事务逻辑:
@Transactional
public void saveOrder(Order order) {
// Spring通过AOP在方法前后添加事务逻辑
}
实际执行流程:
调用方法 → AOP代理拦截 → 开启事务 → 执行目标方法 → 提交/回滚事务
ThreadLocal事务绑定
每个线程有独立的 ThreadLocalMap,用于存储事务相关资源:
// 事务绑定到当前线程,确保同一线程内多个操作共享同一事务
TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
ThreadLocal存储的内容包括:
- 当前数据库连接
- 事务同步回调
- 事务隔离级别
1.4 核心实现流程
// AbstractPlatformTransactionManager 核心逻辑
public final TransactionStatus getTransaction(TransactionDefinition def) {
// 1. 获取现有事务或创建新事务
Object transaction = doGetTransaction();
// 2. 检查传播行为
if (isExistingTransaction(transaction)) {
return handleExistingTransaction(def, transaction);
}
// 3. 开启新事务
startTransaction(def, transaction);
// 4. 设置隔离级别
applyIsolationLevel(transaction, def.getIsolationLevel());
return newTransactionStatus(def, transaction);
}
1.5 回滚机制
Spring默认只对 RuntimeException 和 Error 进行回滚:
// TransactionAspectSupport 处理回滚
private void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
// 检查异常是否触发回滚(默认 RuntimeException 回滚)
if (txInfo.transactionAttribute.rollbackOn(ex)) {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} else {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
1.6 DataSourceTransactionManager具体实现
protected Object doGetTransaction() {
// 从 ThreadLocal 获取当前连接
ConnectionHolder holder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(dataSource);
return new DataSourceTransactionObject(holder);
}
protected void doBegin(Object transaction, TransactionDefinition def) {
Connection con = dataSource.getConnection();
con.setAutoCommit(false); // 关键:关闭自动提交
// 绑定到 ThreadLocal
TransactionSynchronizationManager.bindResource(dataSource,
new ConnectionHolder(con));
}
1.7 实现原理总结
| 原理 | 说明 |
|---|---|
| AOP代理 | 通过 @Transactional 注解自动代理,方法前后添加事务逻辑 |
| ThreadLocal | 将数据库连接绑定到线程,确保同线程共享事务 |
| Connection.setAutoCommit(false) | 核心开关,手动控制提交/回滚 |
| 传播行为 | 通过 ThreadLocal 状态判断是否加入/新建事务 |
| 异常回滚规则 | 默认 RuntimeException 回滚,可配置 |
二、事务传播行为详解
Spring定义了7种事务传播行为,定义在 Propagation 枚举中:
public enum Propagation {
REQUIRED(0), // 默认
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
2.1 REQUIRED(默认)
含义:有事务就加入,没有就新建
| 场景 | 行为 |
|---|---|
| 当前无事务 | 新建事务 |
| 当前有事务 | 加入现有事务 |
适用场景:大多数业务场景
2.2 SUPPORTS
含义:有事务就加入,没有就非事务运行
| 场景 | 行为 |
|---|---|
| 当前无事务 | 非事务执行 |
| 当前有事务 | 加入现有事务 |
适用场景:查询方法,可复用已有事务
2.3 MANDATORY
含义:必须有事务,否则抛异常
| 场景 | 行为 |
|---|---|
| 当前无事务 | 抛 IllegalTransactionStateException |
| 当前有事务 | 加入现有事务 |
适用场景:强制要求在事务上下文中调用,防止误用
2.4 REQUIRES_NEW
含义:总是新建事务,挂起现有事务
| 场景 | 行为 |
|---|---|
| 当前无事务 | 新建事务 |
| 当前有事务 | 挂起现有事务,新建独立事务 |
适用场景:独立日志记录、独立子任务(失败不影响主事务)
2.5 NOT_SUPPORTED
含义:非事务执行,挂起现有事务
| 场景 | 行为 |
|---|---|
| 当前无事务 | 非事务执行 |
| 当前有事务 | 挂起现有事务,非事务执行 |
适用场景:耗时操作、不需要事务的操作
2.6 NEVER
含义:必须非事务,否则抛异常
| 场景 | 行为 |
|---|---|
| 当前无事务 | 非事务执行 |
| 当前有事务 | 抛 IllegalTransactionStateException |
适用场景:严格要求非事务环境
2.7 NESTED
含义:嵌套事务(基于保存点)
| 场景 | 行为 |
|---|---|
| 当前无事务 | 新建事务 |
| 当前有事务 | 创建保存点,成为嵌套事务 |
特点:
- 子事务回滚只回滚到保存点,不影响父事务
- 父事务回滚会连带子事务回滚
适用场景:需要部分回滚的业务逻辑
2.8 传播行为示意图
┌─────────────────────────────────────────────────────────────┐
│ REQUIRED(加入) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 父事务 │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 子方法(加入同一事务) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ 一起提交或回滚 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ REQUIRES_NEW(新建) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ 父事务(挂起) │ ←──→ │ 子事务(独立) │ │
│ │ Connection A │ │ Connection B │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ 父子独立,各自提交/回滚 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ NESTED(嵌套) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 事务 │ │
│ │ Savepoint ──────────────────────┐ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 子事务(嵌套) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ 子事务回滚到Savepoint,父事务继续;父回滚则子也回滚 │
└─────────────────────────────────────────────────────────────┘
2.9 实现原理
核心实现在 AbstractPlatformTransactionManager.handleExistingTransaction():
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction) {
// 1. NEVER - 有事务就抛异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException("Existing transaction found for NEVER propagation");
}
// 2. NOT_SUPPORTED - 挂起事务,非事务执行
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
Object suspendedResources = suspend(transaction);
return newTransactionStatus(definition, null, suspendedResources);
}
// 3. REQUIRES_NEW - 挂起现有,新建独立事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
return startTransaction(definition, transaction, suspendedResources);
} catch (Exception ex) {
resume(transaction, suspendedResources); // 新事务失败,恢复原事务
throw ex;
}
}
// 4. NESTED - 创建保存点
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException("Nested transactions not supported");
}
// 创建保存点
Object hold = ((SavepointManager) transaction).createSavepoint();
return newTransactionStatus(definition, transaction, hold);
}
// 5. REQUIRED/SUPPORTS/MANDATORY - 加入现有事务
return newTransactionStatus(definition, transaction);
}
挂起事务(suspend)
protected SuspendedResourcesHolder suspend(Object transaction) {
// 1. 从 ThreadLocal 解绑资源
ConnectionHolder holder = (ConnectionHolder)
TransactionSynchronizationManager.unbindResource(dataSource);
// 2. 暂存当前事务状态
List<TransactionSynchronization> synchronizations =
TransactionSynchronizationManager.getSynchronizations();
// 3. 清空 ThreadLocal
TransactionSynchronizationManager.clear();
// 4. 返回挂起资源,以便后续恢复
return new SuspendedResourcesHolder(holder, synchronizations);
}
恢复事务
protected void resume(Object transaction, SuspendedResourcesHolder suspendedResources) {
// 重新绑定到 ThreadLocal
TransactionSynchronizationManager.bindResource(dataSource, suspendedResources.connectionHolder);
// 恢复同步回调
for (TransactionSynchronization sync : suspendedResources.synchronizations) {
TransactionSynchronizationManager.registerSynchronization(sync);
}
}
嵌套事务保存点
// 创建保存点
public Object createSavepoint() throws SQLException {
return connection.setSavepoint("SAVEPOINT_" + savepointCounter++);
}
// 回滚到保存点
public void rollbackToSavepoint(Object savepoint) throws SQLException {
connection.rollback((Savepoint) savepoint);
connection.releaseSavepoint((Savepoint) savepoint);
}
2.10 使用示例
@Service
public class OrderService {
@Autowired
private LogService logService;
// REQUIRED(默认)
@Transactional
public void createOrder(Order order) {
orderDao.save(order);
logService.saveLog(order); // 加入同一事务
}
}
@Service
public class LogService {
// REQUIRES_NEW:即使订单失败,日志也保存
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(Order order) {
logDao.save(new Log(order));
}
// NESTED:部分回滚
@Transactional(propagation = Propagation.NESTED)
public void processItem(Item item) {
// 失败只回滚到保存点,不影响整体
itemDao.process(item);
}
}
2.11 传播行为注意事项
| 传播行为 | 注意点 |
|---|---|
REQUIRES_NEW |
使用独立连接,性能开销较大 |
NESTED |
需要 JDBC3.0+ 支持,部分数据库不支持保存点 |
MANDATORY/NEVER |
用于约束调用方,避免误用 |
NOT_SUPPORTED |
挂起事务期间的操作不受事务保护 |
三、事务失效场景全解析
了解事务失效的场景对于正确使用Spring事务至关重要。
3.1 方法访问权限问题
失效场景
@Service
public class UserService {
@Transactional
private void updateUser() { // ❌ private 方法事务失效
userDao.update(user);
}
@Transactional
protected void saveUser() { // ❌ protected 也可能失效
userDao.save(user);
}
@Transactional
public void deleteUser() { // ✅ public 正常生效
userDao.delete(user);
}
}
原因分析
Spring AOP 使用 CGLIB 或 JDK 动态代理:
- CGLIB:基于继承,
private/protected/final方法无法被代理 - JDK动态代理:基于接口,只能代理接口方法
// CGLIB 生成的代理类
public class UserService$$Proxy extends UserService {
@Override
public void deleteUser() { // ✅ 可以重写 public
try {
transactionManager.begin();
super.deleteUser();
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollback();
}
}
// private 方法无法重写,直接调用目标类,绕过代理
}
3.2 同类内部方法自调用
失效场景
@Service
public class OrderService {
public void createOrder() {
this.saveOrder(); // ❌ 自调用,事务失效
this.saveLog(); // ❌ 自调用,事务失效
}
@Transactional
public void saveOrder() {
orderDao.save(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
logDao.save(log);
}
}
原因分析
自调用绕过了代理对象:
正常调用链:
Controller → Proxy.saveOrder() → TransactionInterceptor → Target.saveOrder()
自调用链:
Target.createOrder() → Target.saveOrder() ← 直接调用,跳过代理!
解决方案
方案一:注入自身代理
@Service
public class OrderService {
@Autowired
private OrderService self; // 注入代理对象
public void createOrder() {
self.saveOrder(); // ✅ 通过代理调用
self.saveLog();
}
}
方案二:使用AopContext
@Service
public class OrderService {
public void createOrder() {
OrderService proxy = (OrderService) AopContext.currentProxy();
proxy.saveOrder(); // ✅ 获取当前代理
}
}
// 启用配置
@EnableAspectJAutoProxy(exposeProxy = true)
3.3 异常被捕获未抛出
失效场景
@Service
public class UserService {
@Transactional
public void updateUser() {
try {
userDao.update(user);
throw new RuntimeException("更新失败"); // 异常被捕获
} catch (Exception e) {
log.error("更新失败", e); // ❌ 静默处理,事务不回滚
}
}
@Transactional
public void saveUser() {
try {
userDao.save(user);
if (user == null) {
throw new Exception("用户为空"); // checked异常
}
} catch (Exception e) {
throw new RuntimeException(e); // ✅ 转换为RuntimeException
}
}
}
原因分析
Spring 默认只对 RuntimeException 和 Error 回滚:
// TransactionAspectSupport 默认规则
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
解决方案
方案一:抛出RuntimeException
@Transactional
public void updateUser() {
try {
userDao.update(user);
} catch (Exception e) {
throw new RuntimeException(e); // ✅ 抛出 RuntimeException
}
}
方案二:指定回滚异常类型
@Transactional(rollbackFor = Exception.class) // ✅ 所有异常都回滚
public void updateUser() {
try {
userDao.update(user);
} catch (Exception e) {
// 即使是 checked 异常也回滚
}
}
3.4 抛出Checked异常
失效场景
@Service
public class UserService {
@Transactional
public void saveUser() throws IOException {
userDao.save(user);
throw new IOException("IO异常"); // ❌ checked异常,默认不回滚
}
@Transactional
public void updateUser() throws SQLException {
userDao.update(user);
throw new SQLException("SQL异常"); // ❌ checked异常,默认不回滚
}
}
原因分析
Checked异常(Exception子类但非RuntimeException)默认不触发回滚。
解决方案
@Transactional(rollbackFor = Exception.class) // ✅ 指定回滚所有异常
public void saveUser() throws IOException {
userDao.save(user);
throw new IOException("IO异常");
}
3.5 数据库引擎不支持事务
失效场景
@Service
public class UserService {
@Transactional
public void saveUser() {
userDao.save(user); // MySQL MyISAM 表,不支持事务
}
}
原因分析
MySQL MyISAM 引擎不支持事务,只有 InnoDB 支持。
验证与修复
-- 查看表引擎
SHOW TABLE STATUS LIKE 'user';
-- 修改引擎
ALTER TABLE user ENGINE = InnoDB;
3.6 多线程调用
失效场景
@Service
public class UserService {
@Transactional
public void batchUpdate(List<User> users) {
new Thread(() -> {
userDao.update(users.get(0)); // ❌ 新线程,事务不共享
}).start();
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
userDao.update(users.get(1)); // ❌ 新线程,事务不共享
});
}
}
原因分析
事务绑定到 ThreadLocal,不同线程有独立的事务上下文:
// TransactionSynchronizationManager
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 每个线程独立的 ThreadLocalMap
Thread A → Connection1 (事务1)
Thread B → Connection2 (无事务/独立事务)
3.7 未被Spring管理
失效场景
// ❌ 没有 @Service/@Component 注解
public class UserService {
@Transactional
public void saveUser() {
userDao.save(user);
}
}
// 或者手动创建对象
UserService userService = new UserService(); // ❌ 不是Spring Bean
userService.saveUser();
原因分析
@Transactional 是Spring注解,只有Spring管理的Bean才会被AOP处理。
3.8 事务传播行为设置不当
失效场景
@Service
public class UserService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveUser() { // ❌ 明确指定非事务执行
userDao.save(user);
}
@Transactional(propagation = Propagation.NEVER)
public void updateUser() { // ❌ 有事务就抛异常
userDao.update(user);
}
}
3.9 事务管理器配置错误
失效场景
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSourceA() { return ...; }
@Bean
public DataSource dataSourceB() { return ...; }
// ❌ 只配置了一个事务管理器,但使用了另一个数据源
@Bean
public PlatformTransactionManager transactionManagerA() {
return new DataSourceTransactionManager(dataSourceA());
}
}
@Service
public class UserService {
@Transactional // 默认使用 transactionManagerA
public void saveUser() {
dataSourceB.save(user); // ❌ 操作的是 dataSourceB,事务不生效
}
}
解决方案
@Transactional("transactionManagerB") // ✅ 指定事务管理器
public void saveUser() {
dataSourceB.save(user);
}
3.10 final/static 方法
失效场景
@Service
public class UserService {
@Transactional
public final void saveUser() { // ❌ final 无法代理
userDao.save(user);
}
@Transactional
public static void updateUser() { // ❌ static 无法代理
userDao.update(user);
}
}
原因分析
CGLIB 通过继承代理,final/static 方法无法被重写。
3.11 事务失效场景总结表
| 失效场景 | 原因 | 解决方案 |
|---|---|---|
| private/protected方法 | CGLIB无法代理非public方法 | 使用public方法 |
| 同类自调用 | 绕过代理,直接调用目标对象 | 注入自身代理 / AopContext |
| 异常被catch | 未抛出异常,Spring无法感知 | 抛出异常或手动回滚 |
| Checked异常 | 默认只回滚RuntimeException | rollbackFor = Exception.class |
| MyISAM引擎 | 数据库不支持事务 | 使用InnoDB |
| 多线程 | ThreadLocal线程隔离 | 同线程执行或独立事务管理 |
| 非Spring Bean | 未被AOP处理 | 添加@Service/@Component |
| NOT_SUPPORTED/NEVER | 明确指定非事务 | 修改传播行为 |
| 多数据源配置错误 | 事务管理器与数据源不匹配 | 指定正确的transactionManager |
| final/static方法 | CGLIB无法重写 | 移除final/static修饰 |
3.12 快速诊断清单
当发现事务不生效时,可以按以下清单逐一排查:
1. 方法是否为 public?
2. 类是否有 @Service/@Component?
3. 是否同类内部调用?(用 self.xxx() 或 AopContext)
4. 异常是否被 catch 后未抛出?
5. 是否抛出 checked 异常?(加 rollbackFor)
6. 数据库是否为 InnoDB?
7. 是否多线程调用?
8. 传播行为是否正确?
9. final/static 方法?
10. 多数据源时事务管理器是否匹配?
四、总结
Spring事务管理是Java开发中不可或缺的知识点。本文从三个维度进行了深度解析:
- 实现原理:理解AOP代理和ThreadLocal绑定机制,掌握事务的核心运作方式
- 传播行为:熟悉7种传播行为的含义和使用场景,正确选择事务传播策略
- 失效场景:牢记10+种常见失效原因,避免踩坑,写出可靠的事务代码
掌握这些内容,能够帮助开发者:
- 正确使用Spring事务
- 快速诊断事务问题
- 编写健壮的业务代码
参考资料
本文为个人学习总结,如有错误请指正。