@Transactional 声明式事务源码概览

在分析@Transactional声明式事务之前,我们先回顾一下传统的基于 JDBC 的事务使用方式。

传统 JDBC 事务控制

在 Spring 出现之前,我们通常需要手动管理事务,代码示例如下:

public class UserService {
    
    public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
        Connection connection = null;
        try {
            // 1. 获取数据库连接
            connection = dataSource.getConnection();
            
            // 2. 开启事务
            connection.setAutoCommit(false);
            
            // 3. 执行业务逻辑
            // 扣减转出账户余额
            String updateFromSql = "UPDATE account SET balance = balance - ? WHERE account_no = ?";
            PreparedStatement ps1 = connection.prepareStatement(updateFromSql);
            ps1.setBigDecimal(1, amount);
            ps1.setString(2, fromAccount);
            ps1.executeUpdate();
            
            // 增加转入账户余额
            String updateToSql = "UPDATE account SET balance = balance + ? WHERE account_no = ?";
            PreparedStatement ps2 = connection.prepareStatement(updateToSql);
            ps2.setBigDecimal(1, amount);
            ps2.setString(2, toAccount);
            ps2.executeUpdate();
            
            // 4. 提交事务
            connection.commit();
            
        } catch (Exception e) {
            // 5. 发生异常时回滚事务
            if (connection != null) {
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            throw new RuntimeException("转账失败", e);
        } finally {
            // 6. 关闭连接
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

使用 Spring 声明式事务

使用@Transactional注解后,同样的业务逻辑变得非常简洁(假设使用 MyBatis):

@Service
public class UserService {
    
    @Transactional
    public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
        // 扣减转出账户余额
        mapper.updateFromSql(updateFromSql, amount, fromAccount);
        
        // 增加转入账户余额
        String updateToSql = "UPDATE account SET balance = balance + ? WHERE account_no = ?";
        mapper.updateToSql(updateToSql, amount, toAccount);
    }
}

Spring 的声明式事务本质上是通过 AOP,将传统 JDBC 事务控制的模板代码封装到切面中:

  1. 切面(Aspect)TransactionInterceptor作为事务切面
  2. 切点(Pointcut):通过@Transactional注解标识需要事务的方法
  3. 通知(Advice):在方法执行前开启事务,执行后提交事务,异常时回滚事务

这样,Spring 就能在运行时动态地为标注了@Transactional的方法添加事务控制逻辑,而不需要开发者手动编写事务管理代码。

下面看看源码,了解@Transactional注解是如何被 Spring 处理的。

源码解析

这里是用来调试的示例代码:

@SpringBootTest
class UserServiceTest {
    @Resource
    private UserService userService;

    @Test
    void findList() {
        userService.findList();
    }
}

其中 findList 方法添加了@Transactional注解:

@Transactional(readOnly = true, rollbackFor = Exception.class)
public void findList() {
    userMapper.findList(new User());
}

Bean 初始化

在 Bean 初始化的时候,Spring 会检查是否存在切面可处理该 Bean,如果有,则用切面创建 Bean 的代理对象:

image-20250713225959804

对于加了@Transactional注解的对象,其会被BeanFactoryTransactionAttributeSourceAdvisor包装,处理逻辑封装在TransactionInterceptor中:

image-20250713231042056

方法调用

Debug 启动测试,跳入加了事务注解的方法:

image-20250713231717510

会发现实际调用的是CglibAopProxy.DynamicAdvisedInterceptor#intercept,且其中会获取方法的拦截器链,拦截器链中有负责处理事务的TransactionInterceptor

image-20250713232114336

之后会将拦截器链作为参数,创建CglibMethodInvocation并调用其proceed方法:

image-20250713232225824

进入proceed方法:

image-20250713232309352

再进入父类的proceed方法,其内部会逐个调用拦截器:

image-20250713232518873

进入TransactionInterceptor其会调用invokeWithinTransaction

image-20250713232629417

该方法包含了事务控制的模板代码:

image-20250714191753470

进入该方法,首先会获取TransactionAttributeSource,表示事务属性的来源,这里的来源为@Transactional注解,通过AnnotationTransactionAttributeSource表示:

image-20250714192505532

之后调用TransactionAttributeSource#getTransactionAttribute方法获取TransactionAttribute,可以看到其中封装了@Transactional注解所定义的事务相关属性:

image-20250714193459230

下面调用determineTransactionManager获取TransactionManager

image-20250714194007162

TransactionManager接口中未定义方法,常见的为其子接口PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}

进入determineTransactionManager方法,可以看到从容器中获取到JdbcTransactionManager

image-20250714194321968

JdbcTransactionManager继承自DataSourceTransactionManager,DataSourceTransactionManager继承自AbstractPlatformTransactionManager,AbstractPlatformTransactionManager实现了PlatformTransactionManager接口,所以后面可以看到将其转为了PlatformTransactionManager类型:

image-20250714195353863

之后调用createTransactionIfNecessary方法创建事务:

image-20250714195433630

进入该方法,调用了PlatformTransactionManager#getTransaction获取事务,实际调用的是AbstractPlatformTransactionManager#getTransaction

image-20250714195655629

进入该方法,其调用了AbstractPlatformTransactionManager#doGetTransaction

image-20250714200546743

实际调用的是DataSourceTransactionManager#doGetTransaction,方法内部创建了DataSourceTransactionObject实例表示事务,但是此时DataSourceTransactionObject中的ConnectionHolder为 null,实际上此时还没有开启事务:

image-20250714200826259

TransactionSynchronizationManager.getResource(obtainDataSource())在获取和DataSource绑定的资源,这里也就是ConnectionHolder,前面没有保存过,所以这里返回空。

obtainDataSource()返回的即当前所使用的数据库连接池,比如DruidDataSource

image-20250714201111898

TransactionSynchronizationManager#getResource最终是从ThreadLocal获取绑定的值:

image-20250714201305081

回到AbstractPlatformTransactionManager#getTransaction,其调用到AbstractPlatformTransactionManager#startTransaction

image-20250714201451046

该方法内部调用了AbstractPlatformTransactionManager#doBegin,具体调用到的是DataSourceTransactionManager#doBegin

image-20250714201850764

进入该方法,其中获取了Connection,然后创建ConnectionHolder保存到DataSourceTransactionObject中:

image-20250714202145590

然后调用DataSourceUtils#prepareConnectionForTransaction等设置连接是否只读和事务隔离级别等:

image-20250714202834480

然后调用TransactionSynchronizationManager#bindResource绑定DataSourceConnectionHolder

image-20250714202949442
image-20250714203108118

回到AbstractPlatformTransactionManager#startTransaction,之后调用了AbstractPlatformTransactionManager#prepareSynchronization保存当前线程的事务信息,可以看到TransactionSynchronizationManager的作用即保存当前线程的事务上下文信息:

image-20250714203317157
image-20250714203414282

回到TransactionAspectSupport#createTransactionIfNecessary,在调用完PlatformTransactionManager#getTransaction后调用TransactionAspectSupport#prepareTransactionInfo创建TransactionInfo对象并返回:

image-20250714203643546
image-20250714203719246

此时已彻底创建完事务,开始调用业务逻辑方法:

image-20250714203806105
image-20250714203839685
image-20250714203857082
image-20250714204030053
image-20250714204045537
image-20250714204100876
image-20250714204301136

在调用完方法之后,最终回到TransactionAspectSupport#invokeWithinTransaction,调用TransactionAspectSupport#commitTransactionAfterReturning完成事务的提交:

image-20250714204644368

具体调用的是PlatformTransactionManager#commit

image-20250714204722703

方法内部调用AbstractPlatformTransactionManager#processCommit

image-20250714204818699

继续调用到AbstractPlatformTransactionManager#doCommit

image-20250714204912637

方法内部即获取到ConnectionHolder中保存的Connection,然后调用其commit方法:

image-20250714205056937

如果业务方法抛出异常,最终由TransactionAspectSupport#completeTransactionAfterThrowing回滚事务,过程类似:

image-20250714205216344

MyBatis 中的事务

那么 Service 方法调用多个 Mapper 方法时,是如何保证多个 Mapper 方法处在同一个事务中的?可以猜测 MyBatis 获取数据库连接时,获取到的即前面事务模板方法中创建的 Connection。

具体来说,MybatisAutoConfiguration会创建SqlSessionFactory

image-20250714235101668
image-20250714235136961
image-20250714235155953
image-20250714235341339

注意buildSqlSessionFactory方法中会创建SpringManagedTransactionFactory,保存在ConfigurationEnvironment中。

之后调用SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)完成对象创建,具体是创建的DefaultSqlSessionFactory

image-20250714235716540

在调用具体 Mapper 方法时,会先调用DefaultSqlSessionFactory#openSessionFromDataSource来创建SqlSession

image-20250715000524974

注意到这里先获取到TransactionFactory,具体也就是前面创建的SpringManagedTransactionFactory,然后调用其TransactionFactory#newTransaction方法创建SpringManagedTransaction

image-20250715000906415

然后将SpringManagedTransaction作为参数创建Executor。Mapper 方法最终是通过SqlSession执行 SQL,而SqlSession是通过这里创建的Executor来执行 SQL,比如查询数据,最终会进入SimpleExecutor#doQuery,其中会调用SimpleExecutor#prepareStatement

image-20250715195847660

进入方法,会发现调用了BaseExecutor#getConnection

image-20250715195936426

进入方法,可以看到这里用到了前面创建Executor时设置的SpringManagedTransaction,调用其getConnection方法:

image-20250715200131164

内部逐层调用了SpringManagedTransaction#openConnectionDataSourceUtils#getConnectionDataSourceUtils#doGetConnection方法:

image-20250715201114577
image-20250715201145378
image-20250715201440551

进一步调用TransactionSynchronizationManager#getResource获取到ConnectionHolder,然后从ConnectionHolder获取到连接:

image-20250715201801346

TransactionSynchronizationManager#getResource获取到的即事务开启时保存在ThreadLocal中的DataSource对应的ConnectionHolder

image-20250715202015628
image-20250715202050959

因为获取到的是开启事务时创建的Connection,所以 MyBatis 执行 SQL 处在 Spring 的事务控制下。

Spring 编程式事务

有时候我们也会用到编程式事务,也就是自行编写 AOP 中的模板方法,在方法内显式提交或回滚:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createUserWithTransaction(String name) {
        // 创建事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            User user = new User();
            user.setName(name);
            userMapper.insertUser(user);

            // 这里可以添加其他数据库操作

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e; // 重新抛出异常
        }
    }
}

DefaultTransactionDefinition定义了默认的事务隔离级别等,必要时可自行调整,经过前面的分析已经知道,大致的流程即getTransaction方法按DefaultTransactionDefinition创建对应的ConnectionPlatformTransactionManager#commit方法内部会调用Connectioncommit方法完成提交。

常见的还有如下方式:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createUserWithTransaction(String name) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(org.springframework.transaction.TransactionStatus status) {
                try {
                    User user = new User();
                    user.setName(name);
                    userMapper.insertUser(user);

                    // 这里可以添加其他数据库操作

                } catch (Exception e) {
                    // 如果发生异常,事务将自动回滚
                    status.setRollbackOnly();
                    throw e; // 重新抛出异常
                }
                return null;
            }
        });
    }
}

进入TransactionTemplate#execute,可以看到方法和前面类似,基于PlatformTransactionManager控制事务:

image-20250715204450536
posted @ 2025-07-15 20:46  Higurashi-kagome  阅读(51)  评论(0)    收藏  举报