part3-abstract-platform-transaction-manager
约 6423 字大约 21 分钟
2026-05-24
系列文章:《深入Spring事务与数据源:从注解到数据库连接的全链路解析》
系列一第3篇:AbstractPlatformTransactionManager源码深度解析
基于版本:Spring Framework 5.3.x
前置阅读:第二篇:TransactionInterceptor如何开启一个事务
前言
上一篇文章咱们追到了tm.getTransaction(txAttr)这一步,然后我就卖了个关子,说这里头学问大了去了。今天,咱们就来把这个关子给圆上!
你可能不知道,PlatformTransactionManager这个接口有三个最重要的方法:
getTransaction():获取或创建事务commit():提交事务rollback():回滚事务
而这三个方法的具体实现,都在AbstractPlatformTransactionManager这个抽象类里。说实话,这个类可是Spring事务管理的"大脑",事务的生命周期、传播行为的处理、事务的挂起和恢复,全在这里头!
而且我敢说,搞懂了这个类,你对Spring事务的理解就能超过80%的Java开发。不信?跟着我往下看!
一、再回顾一下:Spring Boot是怎么配置事务管理器的?
在深入源码之前,咱们还是得先搞明白:事务管理器是哪儿来的?
你还记得上篇文章看的TransactionAutoConfiguration吗?当时咱们只看了@EnableTransactionManagement的部分,但其实它还有另一个重要职责——创建默认的事务管理器!
核心入口1:TransactionAutoConfiguration创建事务管理器
文件位置:spring-boot-autoconfigure/TransactionAutoConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(PlatformTransactionManager.class) // 有且仅有一个事务管理器
static class TransactionTemplateConfiguration {
private final PlatformTransactionManager transactionManager;
TransactionTemplateConfiguration(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Bean
@ConditionalOnMissingBean // 如果没有手动创建
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(this.transactionManager);
}
}
}等等,事务管理器到底是谁创建的?看这个@AutoConfigureAfter,它排在DataSourceTransactionManagerAutoConfiguration之后。这说明啥?说明事务管理器是在这个类里创建的!
核心入口2:DataSourceTransactionManagerAutoConfiguration
文件位置:spring-boot-autoconfigure/DataSourceTransactionManagerAutoConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(TransactionProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
static class DataSourceTransactionManagerConfiguration {
private final DataSource dataSource;
private final TransactionProperties transactionProperties;
DataSourceTransactionManagerConfiguration(DataSource dataSource,
TransactionProperties transactionProperties) {
this.dataSource = dataSource;
this.transactionProperties = transactionProperties;
}
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class) // 如果没有手动配置
@ConditionalOnBean(DataSource.class) // 必须有DataSource
public DataSourceTransactionManager transactionManager() {
// 创建DataSourceTransactionManager
DataSourceTransactionManager transactionManager =
new DataSourceTransactionManager(this.dataSource);
// 如果配置了默认超时时间,设置到事务管理器上
if (this.transactionProperties != null
&& this.transactionProperties.getDefaultTimeout() != null) {
transactionManager.setDefaultTimeout(
this.transactionProperties.getDefaultTimeout());
}
return transactionManager;
}
}
}看到了吧?关键点来了——DataSourceTransactionManager!这就是咱们最常用的事务管理器,它继承自AbstractPlatformTransactionManager!
继承关系是这样的:
PlatformTransactionManager (接口)
↓
AbstractPlatformTransactionManager (抽象类,实现了接口)
↓
DataSourceTransactionManager (具体实现,用于JDBC事务)所以,当咱们调用tm.getTransaction(txAttr)的时候,实际执行的是AbstractPlatformTransactionManager.getTransaction()!
扩展场景1:如何自定义事务管理器?
@Configuration
public class TransactionConfig {
@Bean
public PlatformTransactionManager customTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager =
new DataSourceTransactionManager(dataSource);
// 自定义配置
transactionManager.setDefaultTimeout(30); // 默认超时30秒
return transactionManager;
}
}只要你自己创建了一个PlatformTransactionManager类型的Bean,Spring Boot就不会创建默认的,而是用你的。
扩展场景2:为什么要用DataSourceTransactionManager而不是JpaTransactionManager?
这取决于你用的ORM框架:
- JDBC/MyBatis:用
DataSourceTransactionManager - JPA/Hibernate:用
JpaTransactionManager - MongoDB:用
MongoTransactionManager
Spring Boot会根据classpath自动选择合适的,一般不需要你手动配置。
好了,自动装配的部分搞清楚了,现在正式进入咱们的重头戏——AbstractPlatformTransactionManager!
二、AbstractPlatformTransactionManager:事务管理的大脑
先来看看这个类的整体结构:
核心类:AbstractPlatformTransactionManager
文件位置:spring-tx/AbstractPlatformTransactionManager.java
public abstract class AbstractPlatformTransactionManager
implements PlatformTransactionManager, Serializable {
// ========== 事务同步相关配置 ==========
// 是否总是激活事务同步
// 默认是true,意味着默认支持事务同步回调
private boolean transactionSynchronization = SYNCHRONIZATION_ALWAYS;
// 默认超时时间(秒)
// -1表示永不超时
private int defaultTimeout = TIMEOUT_DEFAULT;
// ========== 嵌套事务相关配置 ==========
// 是否允许嵌套事务
// 默认是true,支持PROPAGATION_NESTED传播行为
private boolean nestedTransactionAllowed = true;
// 是否校验现有事务
private boolean validateExistingTransaction = false;
// 传播行为失败时是否全局回滚
private boolean globalRollbackOnParticipationFailure = true;
// 失败后是否提前设置回滚
private boolean failEarlyOnGlobalRollbackOnly = false;
// 完成时是否清理
private boolean cleanupAfterCompletion = true;
// ========== 抽象方法(由子类实现)==========
// 获取事务对象
protected abstract Object doGetTransaction() throws TransactionException;
// 判断是否存在现有事务
protected abstract boolean isExistingTransaction(Object transaction) throws TransactionException;
// 挂起事务
protected abstract Object suspend(Object transaction) throws TransactionException;
// 恢复事务
protected abstract void resume(Object transaction, Object suspendedResources) throws TransactionException;
// 开始新事务
protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;
// 提交事务
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
// 回滚事务
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
// 设置回滚只标记
protected abstract void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException;
// 清理事务
protected abstract void doCleanupAfterCompletion(Object transaction) throws TransactionException;
}看到没?这个类用了一个经典的设计模式——模板方法模式!
它定义了一个完整的事务管理流程(模板),但是把具体的实现细节留给子类去做。比如说:
doGetTransaction():怎么获取事务,JDBC和JPA的实现肯定不一样doBegin():怎么开启事务,JDBC要关闭自动提交,JPA要开启EntityManager事务doCommit():怎么提交,JDBC调connection.commit(),JPA调entityManager.getTransaction().commit()
这就是模板方法模式的精髓:流程我来定,实现你来搞!
扩展场景3:如果我想实现自己的事务管理器怎么办?
比如你想实现一个基于Redis的分布式事务管理器:
public class RedisTransactionManager extends AbstractPlatformTransactionManager {
@Override
protected Object doGetTransaction() {
// 创建Redis事务对象
return new RedisTransactionObject();
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 开启Redis事务:MULTI命令
RedisTransactionObject tx = (RedisTransactionObject) transaction;
tx.getConnection().multi();
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
// 提交Redis事务:EXEC命令
RedisTransactionObject tx = (RedisTransactionObject) status.getTransaction();
tx.getConnection().exec();
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
// 回滚Redis事务:DISCARD命令
RedisTransactionObject tx = (RedisTransactionObject) status.getTransaction();
tx.getConnection().discard();
}
// ... 其他方法
}不过说实话,分布式事务可没那么简单,这只是个简化版的示例。真正的分布式事务得用2PC、TCC、Saga这些方案。
三、getTransaction:事务获取的完整流程
好了,现在咱们来看这个最核心的方法:
核心方法1:getTransaction
文件位置:spring-tx/AbstractPlatformTransactionManager.java
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// ========== 第一步:获取事务对象 ==========
// 调用子类的doGetTransaction()方法
// 这个方法会创建一个事务对象,但还不开始事务
Object transaction = doGetTransaction();
// 缓存事务定义(如果为null)
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// 使用默认的事务定义
// 传播行为:REQUIRED
// 隔离级别:数据库默认
// 超时时间:-1(不超时)
// 只读:false
definition = new DefaultTransactionDefinition();
}
// ========== 第二步:判断是否存在现有事务 ==========
if (isExistingTransaction(transaction)) {
// 已经存在事务,处理现有事务
// 这里会根据传播行为决定怎么处理
return handleExistingTransaction((TransactionDefinition) definition,
transaction, debugEnabled);
}
// ========== 第三步:检查超时时间 ==========
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException(
"Invalid transaction timeout", definition.getTimeout());
}
// ========== 第四步:根据传播行为处理 ==========
// 场景1:PROPAGATION_MANDATORY(必须有事务)
// 如果当前没有事务,就抛异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 场景2:PROPAGATION_REQUIRED(需要事务)
// 场景3:PROPAGATION_REQUIRES_NEW(需要新事务)
// 场景4:PROPAGATION_NESTED(嵌套事务)
// 这三种传播行为,都需要创建新事务
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 挂起资源(如果有)
// 这一步主要是为了PROPAGATION_REQUIRES_NEW准备的
// 但为了统一处理,这里都先挂起
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// ★★★★★ 核心:开始新事务 ★★★★★
return startTransaction((TransactionDefinition) definition, transaction,
debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 如果开启事务失败,恢复挂起的资源
resume(null, suspendedResources);
throw ex;
}
}
// 场景5:PROPAGATION_SUPPORTS(支持事务)
// 场景6:PROPAGATION_NOT_SUPPORTED(不支持事务)
// 场景7:PROPAGATION_NEVER(不能有事务)
// 这三种传播行为,不需要创建新事务
else {
// 如果当前有事务,但传播行为是NEVER,抛异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Transaction propagation 'never' but there is an existing transaction");
}
// 创建一个空的事务状态
// 没有真正的事务,但是返回一个TransactionStatus对象
return prepareTransactionStatus((TransactionDefinition) definition, null,
true, newTransactionSynchronization((TransactionDefinition) definition), null);
}
}这个方法看着长,但逻辑特别清晰:
- 获取事务对象:调子类的
doGetTransaction() - 判断是否有现有事务:如果有,走
handleExistingTransaction() - 如果没有现有事务:根据传播行为决定怎么处理
关键问题:isExistingTransaction是怎么判断的?
核心方法2:isExistingTransaction
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 判断条件:有ConnectionHolder,并且已经有活跃的连接
return (txObject.getConnectionHolder() != null
&& txObject.getConnectionHolder().isTransactionActive());
}很简单,就是看ConnectionHolder里是不是已经有一个活跃的连接。如果有,说明当前线程已经开启了一个事务。
四、七种传播行为的完整实现
好了,现在咱们重点看看七种传播行为是怎么处理的。这可是面试必问的知识点!
4.1 PROPAGATION_REQUIRED(需要事务)
这是最常用的传播行为,也是默认值。
逻辑:
- 如果当前有事务,就加入
- 如果当前没有事务,就创建新事务
代码实现:
// 当前没有事务时,执行到这里
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 创建新事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
// 当前有事务时,执行到这里
// 进入handleExistingTransaction()方法
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
if (debugEnabled) {
logger.trace("Participating in existing transaction");
}
// 返回现有事务的状态
return prepareTransactionStatus(definition, transaction, false, newTransactionSynchronization, null);
}看到没?prepareTransactionStatus的第三个参数是false,表示"这不是一个新事务"。
扩展场景4:PROPAGATION_REQUIRED的坑——内部方法的异常会影响外部事务
@Service
public class UserService {
@Transactional
public void methodA() {
methodB(); // 调用另一个事务方法
System.out.println("methodA执行完毕");
}
@Transactional(propagation = Propagation.REQUIRED) // 默认就是REQUIRED
public void methodB() {
throw new RuntimeException("methodB抛出异常");
}
}当你调用methodA()的时候,会发生什么?
methodA()开启事务- 调用
methodB(),因为当前已经有事务了,所以methodB()加入这个事务 methodB()抛出异常,事务被标记为rollback-only- 异常传播到
methodA(),methodA()也被捕获 - 整个事务回滚!
你说坑不坑? 很多时候开发者以为methodB()回滚就行了,结果把methodA()也给牵连了。
怎么解决? 下篇文章会讲,用try-catch捕获异常,或者用PROPAGATION_REQUIRES_NEW。
4.2 PROPAGATION_REQUIRES_NEW(需要新事务)
逻辑:
- 不管当前有没有事务,都创建新事务
- 如果有现有事务,先挂起
代码实现:
// 当前没有事务时
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 创建新事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
// 当前有事务时,进入handleExistingTransaction()
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction");
}
// ★★★★★ 挂起现有事务 ★★★★★
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 创建新事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
// 如果创建失败,恢复现有事务
resumeAfterBeginException(transaction, null, suspendedResources, beginEx);
throw beginEx;
}
}关键就在_suspend(transaction)_这一行,它会挂起当前事务!
4.3 PROPAGATION_NESTED(嵌套事务)
逻辑:
- 如果当前有事务,创建嵌套事务(savepoint)
- 如果当前没有事务,创建新事务(等同于REQUIRED)
代码实现:
// 当前没有事务时
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 创建新事务(等同于REQUIRED)
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
// 当前有事务时,进入handleExistingTransaction()
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions");
}
if (debugEnabled) {
logger.debug("Creating nested transaction");
}
// ★★★★★ 创建savepoint ★★★★★
if (useSavepointForNestedTransaction()) {
// 使用JDBC的savepoint
DefaultTransactionStatus status = prepareTransactionStatus(
definition, transaction, false, false, true, null);
status.createAndHoldSavepoint();
return status;
}
else {
// 使用完整的事务嵌套(比较少用)
return startTransaction(definition, transaction, debugEnabled, null);
}
}这里的关键是createAndHoldSavepoint(),它会创建一个JDBC savepoint。
扩展场景5:PROPAGATION_NESTED和PROPAGATION_REQUIRES_NEW的区别
@Service
public class UserService {
@Transactional
public void methodA() {
try {
methodB();
} catch (Exception e) {
System.out.println("捕获异常,methodA继续执行");
}
System.out.println("methodA执行完毕");
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
throw new RuntimeException("methodB抛出异常");
}
}PROPAGATION_NESTED:
methodB()回滚到savepoint,不影响methodA()methodA()可以捕获异常,继续执行- 最终
methodA()提交时,methodB()的修改已经回滚了
PROPAGATION_REQUIRES_NEW:
methodB()开启新事务,methodA()的事务被挂起methodB()回滚自己的事务methodA()恢复事务,继续执行- 最终两个事务各自独立提交或回滚
主要区别:NESTED是同一个事务内的savepoint,REQUIRES_NEW是两个独立的事务。
4.4 其他三种传播行为
PROPAGATION_SUPPORTS(支持事务):
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_SUPPORTS) {
// 有事务就加入,没有就不创建
return prepareTransactionStatus(definition, null, true,
newTransactionSynchronization, null);
}PROPAGATION_NOT_SUPPORTED(不支持事务):
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 挂起现有事务
Object suspendedResources = suspend(transaction);
return prepareTransactionStatus(definition, null, true,
newTransactionSynchronization, suspendedResources);
}PROPAGATION_NEVER(不能有事务):
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
if (isExistingTransaction(transaction)) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
return prepareTransactionStatus(definition, null, true,
newTransactionSynchronization, null);
}五、事务的挂起与恢复
刚才一直提到挂起和恢复,现在咱们就来看看这到底是怎么实现的。
核心方法3:suspend
文件位置:spring-tx/AbstractPlatformTransactionManager.java
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction)
throws TransactionException {
// 如果有事务同步回调,先暂停
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 暂停所有的事务同步回调
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// ★★★★★ 调用子类的suspend方法挂起事务资源 ★★★★★
suspendedResources = doSuspend(transaction);
}
// 解绑数据源连接
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
// 返回挂起的资源
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly,
isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// 如果挂起失败,恢复同步回调
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// 事务同步未激活,但是有事务
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// 啥都没有
return null;
}
}这个方法干了三件事:
- 暂停事务同步回调
- 挂起事务资源(调子类的
doSuspend()) - 清空ThreadLocal中的事务信息
核心方法4:doSuspend(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 设置为null,表示没有连接了
txObject.setConnectionHolder(null);
// ★★★★★ 关键:从ThreadLocal中解绑连接 ★★★★★
// unbindResource会返回被解绑的连接
ConnectionHolder suspendedHolder = TransactionSynchronizationManager.unbindResource(
obtainDataSource());
// 返回被挂起的连接
return suspendedHolder;
}看到没?挂起的本质就是从ThreadLocal中移除连接,这样后续的操作就不会用到这个连接了。
核心方法5:resume
文件位置:spring-tx/AbstractPlatformTransactionManager.java
protected final void resume(@Nullable Object transaction,
@Nullable SuspendedResourcesHolder suspendedResources)
throws TransactionException {
if (suspendedResources != null) {
try {
// ★★★★★ 恢复事务资源 ★★★★★
doResume(transaction, suspendedResources.suspendedResources);
// 恢复事务同步回调
doResumeSynchronization(suspendedResources.suspendedSynchronizations);
// 恢复ThreadLocal中的事务信息
TransactionSynchronizationManager.setCurrentTransactionName(suspendedResources.name);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(suspendedResources.readOnly);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
suspendedResources.isolationLevel);
TransactionSynchronizationManager.setActualTransactionActive(suspendedResources.wasActive);
}
catch (RuntimeException | Error ex) {
// 如果恢复失败,重新挂起
doSuspend(transaction);
throw ex;
}
}
}恢复就是把挂起时清空的那些ThreadLocal重新设置回来。
核心方法6:doResume(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
// 将被挂起的连接重新绑定到ThreadLocal
TransactionSynchronizationManager.bindResource(obtainDataSource(),
(ConnectionHolder) suspendedResources);
}超级简单,就是重新绑定到ThreadLocal!
扩展场景6:什么场景下会用到事务挂起?
最典型的就是日志记录:
@Service
public class OrderService {
@Autowired
private LogService logService;
@Transactional
public void createOrder(Order order) {
// 创建订单
orderMapper.insert(order);
try {
// 记录日志(需要独立事务)
logService.saveLog("创建订单:" + order.getId());
} catch (Exception e) {
// 日志记录失败不影响订单创建
System.err.println("日志记录失败");
}
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String message) {
// 开启新事务
logMapper.insert(new Log(message));
// 即使订单事务回滚,日志也会提交
}
}这样,即使订单创建失败回滚了,日志也已经被独立提交了,方便后续排查问题。
六、startTransaction:开启新事务
好了,现在来看开启新事务的流程:
核心方法7:startTransaction
文件位置:spring-tx/AbstractPlatformTransactionManager.java
private TransactionStatus startTransaction(TransactionDefinition definition,
Object transaction,
boolean debugEnabled,
@Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建事务状态
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// ★★★★★ 核心:调用子类的doBegin开启事务 ★★★★★
doBegin(transaction, definition);
// 准备事务同步
prepareSynchronization(status, definition);
return status;
}关键就在doBegin()这个方法,它会真正地开启一个事务。对于JDBC来说,就是关闭自动提交。
核心方法8:doBegin(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// ========== 第一步:检查是否有连接持有者 ==========
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 从数据源获取新连接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 设置连接持有者
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// ========== 第二步:标记连接为同步 ==========
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// ========== 第三步:设置只读标记 ==========
// 如果事务是只读的,设置连接的只读属性
// 数据库可以对只读事务做优化
Boolean previousReadOnly = determinePreviousReadOnly(txObject, con);
Integer previousIsolationLevel = determinePreviousIsolationLevel(txObject, con);
// ========== 第四步:关闭自动提交(最关键的一步!)==========
try {
// ★★★★★ 核心:关闭自动提交,开启手动事务管理 ★★★★★
con.setAutoCommit(false);
}
catch (Throwable ex) {
logger.debug("Could not set autocommit", ex);
// 如果设置失败,释放连接并抛出异常
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
throw new CannotCreateTransactionException("Could not set autocommit", ex);
}
// ========== 第五步:设置超时时间 ==========
// 如果配置了超时时间,设置到连接上
txObject.getConnectionHolder().setTimeoutInSeconds(
determineTimeout(definition));
// ========== 第六步:绑定到ThreadLocal ==========
// ★★★★★ 核心:将连接绑定到TransactionSynchronizationManager的ThreadLocal ==========
// 这样后续的操作都能获取到同一个连接
TransactionSynchronizationManager.bindResource(
obtainDataSource(), txObject.getConnectionHolder());
}
catch (Throwable ex) {
// 如果开启事务失败,释放连接
if (!txObject.hasConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}这个方法就是事务开启的"临门一脚"!核心动作就是con.setAutoCommit(false),关闭了自动提交,从此以后所有的SQL操作都必须手动commit或rollback。
扩展场景7:为什么要关闭自动提交?
MySQL默认的autocommit是true,也就是说每条SQL都会自动提交。但这样没法做事务啊!
比如说:
// 如果autocommit=true
userMapper.insert(user1); // 立即提交,无法回滚
userMapper.insert(user2); // 立即提交,无法回滚关闭autocommit后:
// autocommit=false
userMapper.insert(user1); // 不提交
userMapper.insert(user2); // 不提交
connection.commit(); // 一起提交,要么都成功,要么都失败这才叫事务!
七、prepareTransactionStatus:准备事务状态
开完事务后,要准备一个事务状态对象返回:
核心方法9:prepareTransactionStatus
文件位置:spring-tx/AbstractPlatformTransactionManager.java
protected final DefaultTransactionStatus prepareTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction,
boolean newTransaction, boolean newSynchronization,
boolean debug, @Nullable Object suspendedResources) {
// 创建事务状态对象
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
// 准备事务同步回调
prepareSynchronization(status, definition);
return status;
}八、commit:事务提交的完整流程
业务方法执行成功后,就要提交事务了。咱们来看看提交是怎么做的:
核心方法10:commit
文件位置:spring-tx/AbstractPlatformTransactionManager.java
@Override
public final void commit(TransactionStatus status) throws TransactionException {
// 获取事务状态
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// ========== 第一步:检查是否被编程式地标记为回滚 ==========
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
// 执行回滚
processRollback(defStatus, false);
return;
}
// ========== 第二步:检查是否应该提交 ==========
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only");
}
// 虽然是调用commit,但实际执行回滚
processRollback(defStatus, true);
return;
}
// ========== 第三步:执行提交 ==========
// 标记事务已完成
defStatus.setCompleted();
try {
// ★★★★★ 核心:处理提交 ★★★★★
processCommit(defStatus);
}
catch (RuntimeException | Error ex) {
// 如果提交失败,执行回滚
processRollbackOnCommitFailure(defStatus, ex);
throw ex;
}
}提交的逻辑也很清晰:
- 检查是否被标记为回滚
- 如果没问题,执行提交
- 如果提交失败,回滚
核心方法11:processCommit
文件位置:spring-tx/AbstractPlatformTransactionManager.java
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
// ========== 准备提交 ==========
// 触发beforeCommit回调
prepareForCommit(status);
// 触发同步回调的beforeCommit
triggerBeforeCommit(status);
// 触发同步回调的beforeCompletion
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// ========== 检查是否有savepoint ==========
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 释放savepoint(嵌套事务)
status.releaseHeldSavepoint();
}
// ========== 新事务才需要真正提交 ==========
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// ★★★★★ 核心:调用子类的doCommit提交事务 ★★★★★
doCommit(status);
}
// ========== 检查unexpectedRollback ==========
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
// ========== 提交后处理 ==========
triggerAfterCommit(status);
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
finally {
// 清理事务
cleanupAfterCompletion(status);
}
}
catch (Throwable ex) {
// 如果清理失败
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
throw ex;
}
}关键就在doCommit(status)这一行,实际提交由子类实现。
核心方法12:doCommit(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
// ★★★★★ 核心:调用JDBC的commit方法 ★★★★★
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}就是这么简单,调一下connection.commit()就完事了!
九、rollback:事务回滚的完整流程
如果业务方法抛出异常,就要回滚事务:
核心方法13:rollback
文件位置:spring-tx/AbstractPlatformTransactionManager.java
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 执行回滚
processRollback(defStatus, false);
}核心方法14:processRollback
文件位置:spring-tx/AbstractPlatformTransactionManager.java
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 触发同步回调的beforeCompletion
triggerBeforeCompletion(status);
// ========== 处理savepoint回滚 ==========
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
// 回滚到savepoint(嵌套事务)
status.rollbackToHeldSavepoint();
}
// ========== 新事务回滚 ==========
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// ★★★★★ 核心:调用子类的doRollback回滚事务 ★★★★★
doRollback(status);
}
else {
// 参与现有事务,只标记为回滚
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 设置rollback-only标记
doSetRollbackOnly(status);
}
// ========== 回滚后处理 ==========
try {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
// 清理事务
cleanupAfterCompletion(status);
}
}
catch (Throwable ex) {
throw ex;
}
}
finally {
// 如果需要全局回滚,抛出异常
cleanupAfterCompletion(status);
}
}核心方法15:doRollback(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
// ★★★★★ 核心:调用JDBC的rollback方法 ★★★★★
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}同样简单,调connection.rollback()就行。
十、cleanupAfterCompletion:事务完成后的清理
事务提交或回滚后,需要清理资源:
核心方法16:cleanupAfterCompletion
文件位置:spring-tx/AbstractPlatformTransactionManager.java
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 标记事务为已完成
status.setCompleted();
// 如果需要清理
if (isCleanupAfterCompletion()) {
// 清理事务
cleanupAfterCompletion(status.getTransaction());
// 恢复挂起的资源
SuspendedResourcesHolder suspendedResources = status.getSuspendedResources();
if (suspendedResources != null) {
// 恢复被挂起的事务
resume(status.getTransaction(), suspendedResources);
}
}
}核心方法17:cleanupAfterCompletion(DataSourceTransactionManager实现)
文件位置:spring-jdbc/DataSourceTransactionManager.java
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 从ThreadLocal中移除连接
TransactionSynchronizationManager.unbindResourceIfPossible(obtainDataSource());
// 恢复自动提交
Connection con = txObject.getConnectionHolder().getConnection();
try {
// ★★★★★ 恢复自动提交 ★★★★★
con.setAutoCommit(true);
}
catch (Throwable ex) {
logger.debug("Could not set autocommit", ex);
}
finally {
// 释放连接(归还到连接池)
DataSourceUtils.releaseConnection(con, obtainDataSource());
// 清理连接持有者
txObject.getConnectionHolder().clear();
}
}清理做了三件事:
- 从ThreadLocal解绑连接
- 恢复自动提交(为下次使用做准备)
- 释放连接(归还到连接池)
十一、完整的事务生命周期图
说了这么多,咱们画个图来总结一下完整的事务生命周期:
调用:tm.getTransaction(txAttr)
↓
┌──────────────────────────────────────────┐
│ AbstractPlatformTransactionManager │
│ │
│ 1. doGetTransaction() │
│ → 创建事务对象 │
│ │
│ 2. isExistingTransaction() │
│ → 判断是否有现有事务 │
└─────────────┬────────────────────────────┘
↓
有现有事务?
↙ ↘
是 否
↓ ↓
┌─────────────┐ ┌─────────────────────┐
│ handle │ │ 根据传播行为处理 │
│ Existing │ │ → REQUIRED/REQUIRES │
│ Transaction │ │ _NEW/NESTED │
│ │ │ → startTransaction() │
└──────┬──────┘ └──────────┬──────────┘
↓ ↓
└────────┬───────────┘
↓
┌──────────────────────────────────────┐
│ startTransaction() │
│ │
│ → doBegin() │
│ → 获取连接 │
│ → con.setAutoCommit(false) │
│ → 绑定到ThreadLocal │
│ │
│ → prepareSynchronization() │
│ → 返回TransactionStatus │
└─────────────┬────────────────────────┘
↓
┌──────────────────────────────────────┐
│ 执行业务方法 │
│ → 成功:commit() │
│ → 失败:rollback() │
└─────────────┬────────────────────────┘
↓ (commit路径)
┌──────────────────────────────────────┐
│ commit() │
│ │
│ → 检查rollback-only标记 │
│ → processCommit() │
│ → beforeCommit回调 │
│ → doCommit() │
│ → con.commit() │
│ → afterCommit回调 │
│ → cleanupAfterCompletion() │
│ → 解绑连接 │
│ → con.setAutoCommit(true) │
│ → 释放连接 │
└──────────────────────────────────────┘
↓ (rollback路径)
┌──────────────────────────────────────┐
│ rollback() │
│ │
│ → processRollback() │
│ → beforeCompletion回调 │
│ → doRollback() │
│ → con.rollback() │
│ → afterCompletion回调 │
│ → cleanupAfterCompletion() │
│ → 解绑连接 │
│ → 恢复自动提交 │
│ → 释放连接 │
└──────────────────────────────────────┘十二、关键问题解答
Q1:为什么嵌套事务要用savepoint?
答案:因为嵌套事务是同一个事务内的回滚点,不是独立的事务。用savepoint可以只回滚嵌套部分,不影响外层事务。
Q2:PROPAGATION_REQUIRES_NEW挂起事务时,连接怎么处理?
答案:挂起时,从ThreadLocal中移除连接;新事务会从连接池获取新连接;恢复时,把旧连接重新绑定回ThreadLocal。
Q3:为什么提交或回滚后要恢复autocommit=true?
答案:因为连接是要归还到连接池的,下一个线程拿到这个连接时,应该恢复默认状态,否则会影响后续使用。
Q4:事务传播行为失效的常见原因是什么?
答案:最常见的是同类方法内部调用,没有经过代理对象。比如A方法调用B方法,如果直接调用this.B(),不会经过代理,传播行为就失效了。
十三、总结与伏笔
通过这篇文章的深度剖析,咱们完整理解了事务的生命周期:
- 获取事务:
getTransaction()完整流程 - 七种传播行为:每种传播行为的处理逻辑和适用场景
- 事务挂起与恢复:ThreadLocal的解绑和重新绑定
- 开启事务:
doBegin()关闭autocommit - 提交事务:
processCommit()调用con.commit() - 回滚事务:
processRollback()调用con.rollback() - 清理资源:解绑连接、恢复autocommit、释放连接
但是,咱们还有个关键问题没深入:
连接是从哪儿来的?
obtainDataSource().getConnection()这行代码,到底是怎么获取连接的?- 数据源(DataSource)是怎么工作的?
- 连接池(比如Druid、HikariCP)是怎么管理连接的?
- 为什么连接要放在ThreadLocal里?
这些问题,将在下一个系列《数据源与连接池全链路源码解析》中为你详细解答。到时候咱们会深入到Druid连接池的源码,看看它是怎么管理连接的,怎么保证线程安全的,怎么做连接有效性检测的。
不过,在开启下一个系列之前,咱们系列一还有两篇文章:
- 第4篇:深入
DataSourceTransactionManager,看它是怎么和数据源绑定的 - 第5篇:揭秘
TransactionSynchronizationManager,看看ThreadLocal的魔法
敬请期待!
参考资料
- Spring Framework 5.3.x 源码
spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.javaspring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java
- 《Spring源码深度解析》
- 《Spring技术内幕》