Skip to content

Spring事务详解

要理解Spring事务的原理,我们需要从事务的本质Spring的事务抽象实现机制(AOP、线程绑定)、核心组件(事务管理器、事务定义、事务状态)、事务属性(传播行为、隔离级别)等多个维度逐步拆解。以下是系统的讲解:

一、事务的基础:ACID特性

事务(Transaction)是一组原子性的操作集合,要么全部成功,要么全部失败。它的核心是保证数据的一致性(Consistency),而这依赖于四个关键特性(ACID):

  1. 原子性(Atomicity):操作不可分割,要么全做,要么全不做;
  2. 一致性(Consistency):事务执行前后,数据从一个合法状态转移到另一个合法状态(如转账后双方余额之和不变);
  3. 隔离性(Isolation):多个事务并发执行时,彼此之间互不干扰;
  4. 持久性(Durability):事务提交后,数据的修改永久保存(即使系统崩溃也不丢失)。

数据库(如MySQL InnoDB)本身提供了事务支持,但不同持久层框架(JDBC、Hibernate、MyBatis)的事务API各不相同。Spring事务的核心价值是:统一事务编程模型,屏蔽底层框架差异,让开发者无需关心具体的事务实现细节。

二、Spring事务的核心组件

Spring通过三个核心接口抽象了事务的整个生命周期,这是理解Spring事务的关键:

1. PlatformTransactionManager:事务管理器(核心)

PlatformTransactionManager是Spring事务的核心接口,定义了事务的基本操作(获取、提交、回滚)。它是策略模式的体现——不同的持久层框架对应不同的实现类:

持久层框架对应的PlatformTransactionManager实现类
JDBC/MyBatisDataSourceTransactionManager
HibernateHibernateTransactionManager
JPAJpaTransactionManager
Redis(非关系型)RedisTransactionManager(Spring Data Redis)

接口定义

java
public interface PlatformTransactionManager {
    // 获取事务(根据TransactionDefinition)
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    // 提交事务
    void commit(TransactionStatus status) throws TransactionException;
    // 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

2. TransactionDefinition:事务属性定义

TransactionDefinition定义了事务的属性配置,包括:

  • 传播行为(Propagation):解决“事务方法调用事务方法”时的事务归属问题(最核心的属性);
  • 隔离级别(Isolation):解决并发事务的干扰问题;
  • 超时时间(Timeout):事务的最大执行时间(防止长事务占用资源);
  • 只读属性(Read-Only):标记事务为只读(数据库可优化,如避免写锁)。

接口定义(关键方法):

java
public interface TransactionDefinition {
    // 传播行为(默认REQUIRED)
    int getPropagationBehavior();
    // 隔离级别(默认DEFAULT,即数据库默认)
    int getIsolationLevel();
    // 超时时间(默认-1,无超时)
    int getTimeout();
    // 是否只读(默认false)
    boolean isReadOnly();
}

3. TransactionStatus:事务状态

TransactionStatus代表当前事务的运行状态(如是否新事务、是否有保存点、是否已回滚),是事务执行过程中的“状态快照”。Spring通过它来控制事务的生命周期(如提交、回滚)。

接口定义(关键方法):

java
public interface TransactionStatus extends SavepointManager {
    // 是否是新事务(非嵌套、非加入已有事务)
    boolean isNewTransaction();
    // 是否有保存点(用于嵌套事务)
    boolean hasSavepoint();
    // 设置事务为“仅回滚”(手动触发回滚)
    void setRollbackOnly();
    // 是否已标记为回滚
    boolean isRollbackOnly();
    // 事务是否已完成(提交或回滚)
    boolean isCompleted();
}

三、Spring事务的实现机制

Spring事务分为编程式事务声明式事务,其中声明式事务(基于@Transactional注解)是最常用的方式,底层依赖AOP线程绑定

1. 编程式事务:显式控制事务

编程式事务需要手动调用事务管理器的API(如TransactionTemplate),代码侵入性强,但灵活。

示例:使用TransactionTemplate

java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createUser(User user) {
        // 执行事务逻辑:若抛出异常则回滚
        transactionTemplate.execute(status -> {
            userMapper.insert(user);
            if (user.getName().equals("admin")) {
                // 手动标记回滚
                status.setRollbackOnly();
            }
            return null;
        });
    }
}

原理TransactionTemplate封装了PlatformTransactionManager的操作,execute方法内部会:

  1. 调用getTransaction获取事务;
  2. 执行用户的业务逻辑;
  3. 根据业务逻辑的执行结果(正常/异常)调用commitrollback

2. 声明式事务:基于AOP的无侵入式事务

声明式事务通过@Transactional注解标记需要事务的方法/类,Spring通过AOP动态代理自动为目标方法添加事务逻辑(开启、提交、回滚)。这是Spring事务的核心优势——无代码侵入

(1)声明式事务的核心原理:AOP拦截

Spring会为带有@Transactional的Bean创建代理对象(JDK动态代理或CGLIB代理)。当调用代理对象的方法时,会先执行事务拦截器TransactionInterceptor)的逻辑,再执行目标方法。

事务拦截器的执行流程(关键步骤):

mermaid
sequenceDiagram
    participant Client
    participant Proxy(代理对象)
    participant TransactionInterceptor(事务拦截器)
    participant Target(目标对象)
    participant PlatformTransactionManager(事务管理器)

    Client->>Proxy: 调用方法(如createUser)
    Proxy->>TransactionInterceptor: 触发拦截
    TransactionInterceptor->>PlatformTransactionManager: getTransaction(TransactionDefinition) ① 获取事务
    PlatformTransactionManager->>TransactionInterceptor: 返回TransactionStatus
    TransactionInterceptor->>Target: 执行目标方法(如userMapper.insert) ② 执行业务
    alt 目标方法正常返回
        TransactionInterceptor->>PlatformTransactionManager: commit(TransactionStatus) ③ 提交事务
    else 目标方法抛出异常
        TransactionInterceptor->>PlatformTransactionManager: rollback(TransactionStatus) ③ 回滚事务
    end
    TransactionInterceptor->>Proxy: 返回结果
    Proxy->>Client: 返回结果

(2)事务拦截器的核心逻辑(源码简化)

TransactionInterceptorMethodInterceptor的实现类,其invoke方法是事务逻辑的入口。以下是简化后的核心代码:

java
public class TransactionInterceptor implements MethodInterceptor {
    private PlatformTransactionManager transactionManager;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 1. 获取事务属性(@Transactional的配置)
        TransactionAttribute txAttr = getTransactionAttribute(invocation);
        // 2. 获取事务管理器(如DataSourceTransactionManager)
        PlatformTransactionManager tm = getTransactionManager(txAttr);
        // 3. 获取目标方法的唯一标识(用于日志等)
        String joinpointIdentification = methodIdentification(invocation.getMethod(), invocation.getThis().getClass());

        try {
            // 4. 开启事务:获取TransactionStatus
            TransactionStatus status = tm.getTransaction(txAttr);
            try {
                // 5. 执行目标方法(业务逻辑)
                Object result = invocation.proceed();
                // 6. 提交事务
                tm.commit(status);
                return result;
            } catch (Throwable ex) {
                // 7. 回滚事务(根据异常类型判断是否回滚)
                tm.rollback(status);
                throw ex;
            }
        } finally {
            // 清理资源
        }
    }
}

3. 线程绑定:事务资源的共享

Spring事务的关键实现细节线程绑定——通过TransactionSynchronizationManager(事务同步管理器)将事务资源(如JDBC的Connection、Hibernate的Session)绑定到当前线程ThreadLocal变量中。这样,同一线程内的所有操作(如多个Mapper调用)都会共享同一个事务资源,从而保证事务的原子性。

(1)TransactionSynchronizationManager的核心作用

TransactionSynchronizationManager是一个工具类,内部维护了多个ThreadLocal变量:

  • resources:存储当前线程的事务资源(键是资源工厂,如DataSource;值是资源,如Connection);
  • transactionSynchronizations:存储当前线程的事务同步回调(如TransactionSynchronization);
  • currentTransactionName:当前事务的名称;
  • currentTransactionReadOnly:当前事务是否只读;
  • currentTransactionIsolationLevel:当前事务的隔离级别。

示例:JDBC事务的线程绑定流程 当使用DataSourceTransactionManager时,事务的开启过程会:

  1. DataSource获取一个Connection
  2. 调用connection.setAutoCommit(false)(关闭自动提交);
  3. Connection绑定到TransactionSynchronizationManagerresources中;
  4. 后续的JDBC操作(如JdbcTemplateMyBatis)会从resources中获取该Connection,确保同一事务内的操作使用同一个Connection

关键代码(DataSourceTransactionManagerdoBegin方法)

java
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;

    try {
        // 1. 获取Connection(从DataSource)
        if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = this.dataSource.getConnection();
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        // 2. 关闭自动提交
        con = txObject.getConnectionHolder().getConnection();
        con.setAutoCommit(false);
        // 3. 设置隔离级别(如果不是默认)
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
            con.setTransactionIsolation(definition.getIsolationLevel());
        }
        // 4. 绑定Connection到当前线程
        TransactionSynchronizationManager.bindResource(this.dataSource, txObject.getConnectionHolder());
    } catch (SQLException ex) {
        // 异常处理
    }
}

四、Spring事务的核心属性详解

@Transactional注解的配置决定了事务的行为,以下是最核心的两个属性

1. 事务传播行为(Propagation)

传播行为解决的是**“一个事务方法调用另一个事务方法时,事务如何传递”的问题。Spring定义了7种传播行为(Propagation枚举),最常用的是REQUIREDREQUIRES_NEW**。

传播行为含义场景示例
REQUIRED(默认)当前有事务则加入,没有则新建大多数业务场景(如“创建订单+扣减库存”,两个操作必须在同一事务中)
REQUIRES_NEW无论当前是否有事务,都新建一个独立事务(原事务挂起)。日志记录(即使主事务回滚,日志也需要保留)
SUPPORTS当前有事务则加入,没有则无事务执行。查询操作(可选事务)
MANDATORY当前必须有事务,否则抛出异常。核心业务逻辑(必须在事务中执行)
NOT_SUPPORTED无事务方式执行,若当前有事务则挂起。长耗时操作(如导出Excel,避免占用事务资源)
NEVER不允许在事务中执行,否则抛出异常。纯查询操作(确保不会修改数据)
NESTED嵌套事务(基于保存点Savepoint),若当前有事务则在其内部创建子事务。部分回滚场景(如“下单”失败时,只回滚“扣减库存”,不回滚“生成订单记录”)

示例:传播行为的差异 假设ServiceA.methodA()调用ServiceB.methodB()

  • methodA的传播行为是REQUIREDmethodB的传播行为是REQUIRED同一事务methodB加入methodA的事务);
  • methodA的传播行为是REQUIREDmethodB的传播行为是REQUIRES_NEW两个独立事务methodA的事务挂起,methodB执行完后恢复methodA的事务)。

2. 事务隔离级别(Isolation)

隔离级别解决的是**“并发事务之间的干扰”**问题。数据库的并发操作会导致三类问题:

  • 脏读(Dirty Read):读取到未提交的修改;
  • 不可重复读(Non-Repeatable Read):同一事务内多次读取同一数据,结果不一致(被其他事务修改并提交);
  • 幻读(Phantom Read):同一事务内多次查询同一条件,结果集数量不一致(被其他事务插入/删除)。

Spring的隔离级别对应数据库的隔离级别(Isolation枚举),默认是DEFAULT(使用数据库的默认隔离级别,如MySQL InnoDB默认是可重复读(REPEATABLE READ))。

隔离级别解决的问题数据库支持情况
DEFAULT(默认)依赖数据库所有数据库
READ_UNCOMMITTED无(允许脏读)MySQL、PostgreSQL等
READ_COMMITTED脏读所有主流数据库
REPEATABLE_READ脏读、不可重复读MySQL InnoDB、PostgreSQL等
SERIALIZABLE所有问题(串行执行)所有数据库(性能最低)

注意:隔离级别越高,并发性能越低。需根据业务场景权衡(如金融系统用SERIALIZABLE,普通系统用READ_COMMITTED)。

3. 其他属性

  • timeout:事务的最大执行时间(单位:秒)。若超过时间,Spring会强制回滚事务(防止长事务占用数据库连接);
  • readOnly:标记事务为只读。数据库会优化这类事务(如MySQL InnoDB会避免写锁,Hibernate会关闭一级缓存的刷新);
  • rollbackFor/noRollbackFor:控制事务回滚的异常类型。默认情况下,Spring只回滚RuntimeExceptionError(检查型异常如IOException不会回滚)。如需回滚检查型异常,需设置rollbackFor = Exception.class

五、Spring事务的常见问题与解决方案

1. 事务不生效的常见原因

  • (1)方法不是public:Spring AOP默认只拦截public方法(@Transactional对非public方法无效);
  • (2)类未被Spring管理:未加@Component/@Service等注解,导致Spring无法创建代理对象;
  • (3)内部方法调用:同一类中的方法A调用方法B(B@Transactional),此时是内部调用,不会经过代理对象,事务不生效。解决方案:① 将方法B放到另一个类中;② 注入自身的代理对象(@Autowired自身,或使用AopContext.currentProxy());
  • (4)异常被捕获且未抛出:方法内部try-catch了异常但未重新抛出,Spring无法感知异常,不会回滚。解决方案catch后重新抛出异常(如throw new RuntimeException(e));
  • (5)未开启事务管理:未加@EnableTransactionManagement注解(Spring Boot自动开启,但非Boot项目需手动加);
  • (6)数据源与事务管理器不匹配:如使用MyBatis但配置了HibernateTransactionManager,导致事务无法管理MyBatisSqlSession
  • (7)多线程调用:事务是线程绑定的,子线程中的操作不会加入父线程的事务。解决方案:手动传递事务上下文,或使用分布式事务(如Seata)。

2. 长事务的问题与优化

长事务(如事务中包含远程调用、文件上传、用户等待)会导致:

  • 数据库连接池耗尽(连接被长时间占用);
  • 锁竞争加剧(如InnoDB的行锁不会释放);
  • 事务超时风险增加。

优化方案

  • 缩小事务边界:将非核心操作(如日志、缓存更新)放到事务外(可通过TransactionSynchronization在事务提交后执行);
  • 异步处理:将长耗时操作放到异步线程中(如@Async);
  • 拆分事务:将大事务拆分为多个小事务(需保证最终一致性)。

六、Spring事务与分布式事务

Spring的本地事务(上文讲解的)仅能解决单数据源的事务问题。对于多数据源分布式系统(如微服务),需要使用分布式事务解决方案。

1. 分布式事务的挑战

分布式系统中,事务涉及多个服务/数据源,无法通过本地事务保证ACID。例如:

  • 用户下单(服务A,数据库A)→ 扣减库存(服务B,数据库B)→ 记录日志(服务C,数据库C)。若服务B失败,服务A和C的操作需要回滚,但本地事务无法跨服务/数据源。

2. Spring的分布式事务支持

Spring本身没有实现分布式事务,但整合了主流的分布式事务框架:

  • XA事务(两阶段提交,2PC):通过JtaTransactionManager整合支持XA的数据库/中间件(如Atomikos、Bitronix);
  • TCC(Try-Confirm-Cancel):通过Spring Cloud TCCSeata实现;
  • Saga:通过SeataApache ServiceComb实现;
  • 最终一致性:通过消息队列(如RocketMQ)实现(事务消息)。

示例:Seata的分布式事务 Seata是阿里开源的分布式事务框架,支持XA、TCC、Saga等模式。Spring Boot整合Seata只需:

  1. 引入Seata依赖;
  2. 配置Seata的注册中心(如Nacos);
  3. 在需要分布式事务的方法上添加@GlobalTransactional注解(替代@Transactional)。

七、总结

Spring事务的核心是**“统一抽象+AOP+线程绑定”**:

  1. 统一抽象:通过PlatformTransactionManager屏蔽底层框架差异;
  2. AOP:通过动态代理实现声明式事务,无代码侵入;
  3. 线程绑定:通过TransactionSynchronizationManager将事务资源绑定到当前线程,保证同一事务内的操作共享资源。

理解Spring事务的关键是掌握核心组件的职责(事务管理器、事务定义、事务状态)和事务属性的作用(传播行为、隔离级别)。在实际开发中,需注意事务的边界、异常处理、多线程场景,避免事务不生效或长事务问题。

扩展阅读