@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 事务控制的模板代码封装到切面中:
- 切面(Aspect):
TransactionInterceptor作为事务切面 - 切点(Pointcut):通过
@Transactional注解标识需要事务的方法 - 通知(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 的代理对象:
![]() |
对于加了@Transactional注解的对象,其会被BeanFactoryTransactionAttributeSourceAdvisor包装,处理逻辑封装在TransactionInterceptor中:
![]() |
方法调用
Debug 启动测试,跳入加了事务注解的方法:
![]() |
会发现实际调用的是CglibAopProxy.DynamicAdvisedInterceptor#intercept,且其中会获取方法的拦截器链,拦截器链中有负责处理事务的TransactionInterceptor:
![]() |
之后会将拦截器链作为参数,创建CglibMethodInvocation并调用其proceed方法:
![]() |
进入proceed方法:
![]() |
再进入父类的proceed方法,其内部会逐个调用拦截器:
![]() |
进入TransactionInterceptor其会调用invokeWithinTransaction:
![]() |
该方法包含了事务控制的模板代码:
![]() |
进入该方法,首先会获取TransactionAttributeSource,表示事务属性的来源,这里的来源为@Transactional注解,通过AnnotationTransactionAttributeSource表示:
![]() |
之后调用TransactionAttributeSource#getTransactionAttribute方法获取TransactionAttribute,可以看到其中封装了@Transactional注解所定义的事务相关属性:
![]() |
下面调用determineTransactionManager获取TransactionManager:
![]() |
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:
![]() |
JdbcTransactionManager继承自DataSourceTransactionManager,DataSourceTransactionManager继承自AbstractPlatformTransactionManager,AbstractPlatformTransactionManager实现了PlatformTransactionManager接口,所以后面可以看到将其转为了PlatformTransactionManager类型:
![]() |
之后调用createTransactionIfNecessary方法创建事务:
![]() |
进入该方法,调用了PlatformTransactionManager#getTransaction获取事务,实际调用的是AbstractPlatformTransactionManager#getTransaction:
![]() |
进入该方法,其调用了AbstractPlatformTransactionManager#doGetTransaction:
![]() |
实际调用的是DataSourceTransactionManager#doGetTransaction,方法内部创建了DataSourceTransactionObject实例表示事务,但是此时DataSourceTransactionObject中的ConnectionHolder为 null,实际上此时还没有开启事务:
![]() |
TransactionSynchronizationManager.getResource(obtainDataSource())在获取和DataSource绑定的资源,这里也就是ConnectionHolder,前面没有保存过,所以这里返回空。
obtainDataSource()返回的即当前所使用的数据库连接池,比如DruidDataSource:
![]() |
TransactionSynchronizationManager#getResource最终是从ThreadLocal获取绑定的值:
![]() |
回到AbstractPlatformTransactionManager#getTransaction,其调用到AbstractPlatformTransactionManager#startTransaction:
![]() |
该方法内部调用了AbstractPlatformTransactionManager#doBegin,具体调用到的是DataSourceTransactionManager#doBegin:
![]() |
进入该方法,其中获取了Connection,然后创建ConnectionHolder保存到DataSourceTransactionObject中:
![]() |
然后调用DataSourceUtils#prepareConnectionForTransaction等设置连接是否只读和事务隔离级别等:
![]() |
然后调用TransactionSynchronizationManager#bindResource绑定DataSource和ConnectionHolder:
![]() |
![]() |
回到AbstractPlatformTransactionManager#startTransaction,之后调用了AbstractPlatformTransactionManager#prepareSynchronization保存当前线程的事务信息,可以看到TransactionSynchronizationManager的作用即保存当前线程的事务上下文信息:
![]() |
![]() |
回到TransactionAspectSupport#createTransactionIfNecessary,在调用完PlatformTransactionManager#getTransaction后调用TransactionAspectSupport#prepareTransactionInfo创建TransactionInfo对象并返回:
![]() |
![]() |
此时已彻底创建完事务,开始调用业务逻辑方法:
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
在调用完方法之后,最终回到TransactionAspectSupport#invokeWithinTransaction,调用TransactionAspectSupport#commitTransactionAfterReturning完成事务的提交:
![]() |
具体调用的是PlatformTransactionManager#commit:
![]() |
方法内部调用AbstractPlatformTransactionManager#processCommit:
![]() |
继续调用到AbstractPlatformTransactionManager#doCommit:
![]() |
方法内部即获取到ConnectionHolder中保存的Connection,然后调用其commit方法:
![]() |
如果业务方法抛出异常,最终由TransactionAspectSupport#completeTransactionAfterThrowing回滚事务,过程类似:
![]() |
MyBatis 中的事务
那么 Service 方法调用多个 Mapper 方法时,是如何保证多个 Mapper 方法处在同一个事务中的?可以猜测 MyBatis 获取数据库连接时,获取到的即前面事务模板方法中创建的 Connection。
具体来说,MybatisAutoConfiguration会创建SqlSessionFactory:
![]() |
![]() |
![]() |
![]() |
注意buildSqlSessionFactory方法中会创建SpringManagedTransactionFactory,保存在Configuration的Environment中。
之后调用SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)完成对象创建,具体是创建的DefaultSqlSessionFactory:
![]() |
在调用具体 Mapper 方法时,会先调用DefaultSqlSessionFactory#openSessionFromDataSource来创建SqlSession:
![]() |
注意到这里先获取到TransactionFactory,具体也就是前面创建的SpringManagedTransactionFactory,然后调用其TransactionFactory#newTransaction方法创建SpringManagedTransaction:
![]() |
然后将SpringManagedTransaction作为参数创建Executor。Mapper 方法最终是通过SqlSession执行 SQL,而SqlSession是通过这里创建的Executor来执行 SQL,比如查询数据,最终会进入SimpleExecutor#doQuery,其中会调用SimpleExecutor#prepareStatement:
![]() |
进入方法,会发现调用了BaseExecutor#getConnection:
![]() |
进入方法,可以看到这里用到了前面创建Executor时设置的SpringManagedTransaction,调用其getConnection方法:
![]() |
内部逐层调用了SpringManagedTransaction#openConnection、DataSourceUtils#getConnection、DataSourceUtils#doGetConnection方法:
![]() |
![]() |
![]() |
进一步调用TransactionSynchronizationManager#getResource获取到ConnectionHolder,然后从ConnectionHolder获取到连接:
![]() |
而TransactionSynchronizationManager#getResource获取到的即事务开启时保存在ThreadLocal中的DataSource对应的ConnectionHolder:
![]() |
![]() |
因为获取到的是开启事务时创建的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创建对应的Connection,PlatformTransactionManager#commit方法内部会调用Connection的commit方法完成提交。
常见的还有如下方式:
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控制事务:
![]() |




























































浙公网安备 33010602011771号