目录


一、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默认只对 RuntimeExceptionError 进行回滚:

// 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 默认只对 RuntimeExceptionError 回滚:

// 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开发中不可或缺的知识点。本文从三个维度进行了深度解析:

  1. 实现原理:理解AOP代理和ThreadLocal绑定机制,掌握事务的核心运作方式
  2. 传播行为:熟悉7种传播行为的含义和使用场景,正确选择事务传播策略
  3. 失效场景:牢记10+种常见失效原因,避免踩坑,写出可靠的事务代码

掌握这些内容,能够帮助开发者:

  • 正确使用Spring事务
  • 快速诊断事务问题
  • 编写健壮的业务代码

参考资料


本文为个人学习总结,如有错误请指正。