Spring事务简述
Spring 框架提供了一套完整的事务解决方案,可以通过声明式或者编程事务对数据库操作进行事务管理,本文主要介绍声明式事务原理。
Spring声明式事务管理的原理基于 AOP(面向切面编程) 和 事务管理器(PlatformTransactionManager),通过动态代理和线程绑定资源实现声明式事务控制。
基本原理如下:
- 事务 AOP动态代理:Spring会在容器启动时扫描所有添加了
@Transactional
的方法,创建动态代理对象 - 事务拦截:
TransactionInterceptor
事务拦截器在方法执行前后处理事务逻辑- 开启事务:根据事务属性(传播行为、隔离级别等)获取或创建事务
- 执行业务逻辑:调用原始方法
- 提交/回滚事务:根据执行结果(异常或返回值)决定提交或回滚
Spring 定义了多个事务方法相互调用时,事务如何传播和嵌套的逻辑规则,该规则被称作事务的传播行为。主要包含以下几种:
- REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则启动一个新事务。
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- REQUIRES_NEW:启动一个新事务,如果当前存在事务,则挂起当前事务。
- NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则启动一个新事务。
Spring事务核心接口
Spring事务框架通过抽象不同的事务概念定义了一系列的接口类,这些接口类在整个事务管理中扮演者不同的角色,理解这些接口的含义可以帮助开发更有效的了解Spring事务的底层原理。
PlatformTransactionManager
Spring事务管理的核心接口,它定义了事务管理的基本操作,包括事务的开始、提交和回滚。而最常用的数据源事务管理DataSourceTransactionManager
实现了
public interface PlatformTransactionManager {
// 获取事务状态
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
Spring 提供了多种实现类,用于支持不同类型的事务管理策略
DataSourceTransactionManager
: 数据源事务管理器,用于JDBC,也是最常用的事务管理器JpaTransactionManager
: Jpa 事务管理器HibernateTransactionManager
: Hibernate 事务管理器
TransactionDefinition
TransactionDefinition
接口定义了事务的属性,包括事务的传播行为、隔离级别、超时时间等。
public interface TransactionDefinition {
// 事务传播行为
int getPropagationBehavior();
// 事务隔离级别
int getIsolationLevel();
// 事务超时时间
int getTimeout();
// 是否只读事务
boolean isReadOnly();
}
TransactionAttribute
TransactionAttribute
是 TransactionDefinition
子接口,该接口在其基础上扩展事务管理器属性配置、异常回滚类型等
public interface TransactionAttribute extends TransactionDefinition {
//指定事务管理器
String getQualifier();
//事务标签
Collection<String> getLabels();
//异常回滚类型
boolean rollbackOn(Throwable ex);
}
TransactionAttributeSource
TransactionAttributeSource
是获取事务属性信息的接口,可以通过注解、配置信息等获取指定的事务属性信息。
public interface TransactionAttributeSource {
//是否为候选类
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
// 通过方法或类获取事务属性
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}
AnnotationTransactionAttributeSource
:这是最常用的实现类,用于解析 @Transactional 注解。它会检查方法或类上是否有@Transactional
注解,并将其解析为TransactionAttribute
对象。NameMatchTransactionAttributeSource
:通过方法名称匹配来确定方法的事务属性。它允许通过配置方法名称和对应的事务属性来实现事务管理。
TransactionStatus
TransactionStatus 接口提供了事务的状态信息,包括事务是否已经完成、是否需要回滚以及回滚到指定保存点等。
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable{
// 是否存在保存点
boolean hasSavepoint();
}
public interface TransactionExecution {
//是否是新事务
boolean isNewTransaction();
//标记事务为回滚
void setRollbackOnly();
//事务是否标记为回滚
boolean isRollbackOnly();
//事务是否已经完成
boolean isCompleted();
}
public interface SavepointManager {
//创建保存点
Object createSavepoint() throws TransactionException;
//回滚到保存点
void rollbackToSavepoint(Object savepoint) throws TransactionException;
//释放保存点
void releaseSavepoint(Object savepoint) throws TransactionException;
}
数据库的保存点机制是一种事务管理功能,允许在事务执行过程中设置一个或多个保存点(Savepoint)。如果事务在后续操作中遇到问题,可以回滚到最近的保存点,而不是回滚整个事务。Spring的嵌套事务依赖于该机制实现 SavepointManager 以通用的方式编程式地管理事务保存点,主要为Spring事务功能提供了保存点的创建、回滚和释放功能
TransactionSynchronization
TransactionSynchronization 接口提供了事务同步回调功能,允许在事务的不同阶段自定义逻辑,实现更加灵活的事务管理。
public interface TransactionSynchronization {
// 暂停事务回调
default void suspend() {}
// 恢复事务回调
default void resume() {}
// 刷新session回调
default void flush() {}
// 事务提交前回调
default void beforeCommit(boolean readOnly) {}
// 事务完成前回调
default void beforeCompletion() {}
// 事务提交后回调
default void afterCommit() {}
// 事务完成后回调
default void afterCompletion(int status) {}
}
开发者可以通过TransactionSynchronizationManager
注册自定义的同步回调实现类,Spring事务在执行阶段会调用相应的回调方法。
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 事务提交后自定义逻辑
}
});
TransactionSynchronizationManager
TransactionSynchronizationManager
是一个工具类,用于管理事务同步。它提供了事务上下文的管理功能,例如获取当前事务的状态、注册事务同步回调等,它主要依赖于ThreadLocal 机制保障在不同线程下的资源隔离。
public abstract class TransactionSynchronizationManager {
// 事务资源 ThreadLocal
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 事务同步回调 ThreadLocal
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
// 当前事务名称 ThreadLocal
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
// 只读事务 ThreadLocal
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
// 当前事务隔离级别 ThreadLocal
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
// 事务活跃 ThreadLocal
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
public static boolean isActualTransactionActive() {
// 判断当前是否有活跃的事务
}
public static void registerSynchronization(TransactionSynchronization synchronization) {
// 注册事务同步回调
}
public static Object getResource(Object key) {
// 获取事务资源
}
public static void bindResource(Object key, Object value) {
// 绑定事务资源
}
public static void unbindResource(Object key) {
// 解绑事务资源
}
}
Spring 事务核心原理
Spring 声明式事务主要是通过 @Transactional
注解完成事务声明,可以应用于类或者方法上。这种方式依赖于Spring AOP实现,Spring生成代理过程中检查类或者方法上是否添加了@Transactional
注解,并为其生成代理对象,并在执行事务方法时会执行代理增强从而达到管理事务的能力。
而事务代理增强功能依赖于 TransactionInterceptor
实现,该类也是Spring事务中的核心实现类。
执行事务 invokeWithinTransaction
TransactionInterceptor
实现了 MethodInterceptor
接口,该接口会在执行代理方法时调用 invoke 方法实现方法增强,比如完成事务管理。TransactionInterceptor
实现方法如下:
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取目标类
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 执行 TransactionAspectSupport 的 invokeWithinTransaction 方法
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
事务管理的核心功能由其父类 TransactionAspectSupport
实现,TransactionAspectSupport
invokeWithinTransaction
方法核心源码如下(去除部分非核心代码)。
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 获取事务属性源
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取事务属性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 选择事务管理器
final TransactionManager tm = determineTransactionManager(txAttr);
...
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 生成事务名称
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行下一个拦截器或者目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清理当前事务
cleanupTransactionInfo(txInfo);
}
...
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
...
}
TransactionInfo 包含了事务属性
TransactionAttribute
、事务管理器PlatformTransactionManager
、事务状态TransactionStaus
以及旧事务TransactionInfo等多个属性。
该方法核心步骤:
- 获取事务属性
getTransactionAttributeSource
:通过TransactionAttributeSource
实例获取事务配置属性TransactionAttribute
,通常是解析方法注解@Transactional
配置获取 - 选择事务管理器
determineTransactionManager
:PlatformTransactionManager
,这里默认为数据源事务管理DataSourceTransactionManager
- 创建事务
createTransactionIfNecessary
:假如当前不存在事务则根据属性配置创建事务,否则使用已经存在的事务 - 执行目标方法或者下一个拦截器方法
invocation.proceedWithInvocation()
- 异常回滚
completeTransactionAfterThrowing
:当出现异常则根据异常类型判断是否需要回滚,默认为 RuntimeException。 - 清理事务
cleanupTransactionInfo
:清理当前事务,将旧事务(假如存在)重新设置到 ThreadLocal 本地线程变量中 - 提交事务
commitTransactionAfterReturning
:提交事务完成整个事务操作
下面逐个分解各个操作的处理逻辑
创建事务 createTransactionIfNecessary
查看创建事务的代码,核心就是通过事务管理器 PlatformTransactionManager
根据属性创建事务状态 TransactionStatus
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
...
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 事务管理器根据事务属性生成事务
status = tm.getTransaction(txAttr);
}
}
// 根据各种事务信息创建 TransactionInfo 实例
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
getTransaction方法由PlatformTransactionManager
抽象子类AbstractPlatformTransactionManager
实现,该方法主要是获取当前事务状态对象 TransactionStatus
并开启事务。
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 获取当时事务对象
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 已有事务处理
if (isExistingTransaction(transaction)) {
// 处理已有事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 校验超时必须大于默认值
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 假如不存在事务并且事务传播类型为 PROPAGATION_MANDATORY ,则抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// REQUIRED、REQUIRES_NEW、NESTED 传播行为
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 暂停当前事务,当前为新事务因此为null
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
//开始事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
//异常回退,新事务为null
resume(null, suspendedResources);
throw ex;
}
}
else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
**doGetTransaction **: 获取事务对象DataSourceTransactionObject
,并尝试从ThreadLocal 获取数据库连接,此时为新事务则为null
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 设置是否允许嵌套事务
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 获取ThreadLocal中的连接
// 新事务获取连接为null
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
isExistingTransaction :根据是否存在连接判断是否已存在事务,主要用于NESTED传播行为的嵌套事务与REQUIRES_NEW传播行为的独立事务。
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 新事务此时为false
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
开启事务 startTransaction
startTransaction源码如下:
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建 TransactionStatus 对象
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开始事务
doBegin(transaction, definition);
// 事务同步
prepareSynchronization(status, definition);
return status;
}
doBegin: 该方法的主要作用是获取数据库连接设置相关属性,并将其绑定到线程中,保证数据库连接在当前线程的唯一性
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();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// 关闭数据库连接自动提交
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
// 设置数据库连接只读
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// 设置超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 绑定当前连接到线程
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
// 释放数据库连接
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
prepareSynchronization : 该方法的主要作用是初始化事务同步器,将当前事务的各种状态绑定到 ThreadLocal中,保证事务同步机制的正常运作。
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
// 设置当前事务活跃
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
// 设置当前事务隔离级别
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
// 设置当前事务只读状态
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
// 设置当前事务名称
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
// 初始化事务同步管理器,即isSynchronizationActive = true
TransactionSynchronizationManager.initSynchronization();
}
}
提交事务 commitTransactionAfterReturning
在事务创建并开启后会执行目标方法,当目标方法正常执行后会调用 commitTransactionAfterReturning
方法实现事务提交。
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
该方法最终会调用事务管理器的commit
方法实现事务提交,抽象子类AbstractPlatformTransactionManager
实现了commit 方法。
public final void commit(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;
// 事务状态标记为局部回滚状态,直接回滚
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}
// 事务标记为全局回滚状态,直接回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
processRollback(defStatus, true);
return;
}
// 处理提交
processCommit(defStatus);
}
局部回滚 (LocalRollbackOnly) 和 全局回滚(GlobalRollbackOnly) 是两种不同的回滚标记,用于控制事务的回滚行为.局部回滚用于回滚子事务或嵌套事务,而全局回滚事回滚整个事务链
源码中主要判断逻辑
- 判断局部回滚
LocalRollbackOnly
,处理事务回滚 - 判断全局回滚
GlobalRollbackOnly
,处理事务回滚 - 处理事务提交
关于局部回滚与全局回滚标记作用下文会详细介绍,下面继续查看 processCommit
处理事务提交的源码
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
// 触发事务提交前与完成前TransactionSynchronization同步回调
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
//存在保存点 嵌套事务
if (status.hasSavepoint()) {
unexpectedRollback = status.isGlobalRollbackOnly();
// 提交释放保存点
status.releaseHeldSavepoint();
}
//新事务
else if (status.isNewTransaction()) {
unexpectedRollback = status.isGlobalRollbackOnly();
// 提交事务
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// 当事务已经被标记为全局回滚后显式抛出异常
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// 触发事务完成后TransactionSynchronization同步回调,事务状态为已回滚
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// 控制在事务提交过程中发生异常时是否回滚事务
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
// 触发事务完成后TransactionSynchronization同步回调,事务状态未知
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
// 触发事务提交前与完成前TransactionSynchronization同步回调
// 保证成功执行
triggerBeforeCompletion(status);
}
// 事务提交失败尝试进行回滚
doRollbackOnCommitException(status, ex);
throw ex;
}
try {
// 触发事务提交后TransactionSynchronization同步回调
triggerAfterCommit(status);
}
finally {
// 触发事务完成后TransactionSynchronization同步回调,事务状态为已提交
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
processCommit
提交方法主要逻辑如下:
trigger*
:触发事务不同阶段的TransactionSynchronization
同步回调,注意:该方法只有在当前存在新事务时会执行,嵌套事务依赖于保存点机制因此不会触发,即一个事务只会执行一次回调。
protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
// 当前为新事务时触发
if (status.isNewSynchronization()) {
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
}
}
public static void triggerBeforeCommit(boolean readOnly) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.beforeCommit(readOnly);
}
}
releaseHeldSavepoint
:假如当前事务为嵌套事务则释放保存点doCommit
:操作事务最终提交
doCommit
方法是 AbstractPlatformTransactionManager
提供的模板抽象方法,数据源事务管理器 DataSourceTransactionManager
实现该方法完成数据库事务提交。其本质上是通过获取数据库连接完成数据库事务提交。
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.commit();
}
catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
回滚事务 completeTransactionAfterThrowing
当目标方法执行失败后则会执行completeTransactionAfterThrowing
方法,该方法主要逻辑是根据方法抛出的异常判断是否需要回滚否则正常提交事务。
比如事务注解@Transactional(rollbackFor=CustomException.class)
中指定异常,假如事务方法抛出CustomException
自定义异常,执行事务回滚,否则正常提交事务忽略其他异常。
回滚操作 rollback
也是通过事务管理器AbstractPlatformTransactionManager
实现
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 满足回滚异常执行回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
...
}
else {
try {
// 无需回滚依然提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
...
}
}
}
rollback方法与commit方法类似,通过调用 processRollback 处理回滚操作。
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 触发事务完成前TransactionSynchronization同步回调
triggerBeforeCompletion(status);
// 存在保存点调则回滚到保存点,用于嵌套事务
if (status.hasSavepoint()) {
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
// 当前事务为新事务则执行回滚操作
doRollback(status);
}
else {
// 当前存在事务标记事务为RollbackOnly
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
doSetRollbackOnly(status);
}
}
// 检查是否在事务被全局标记为只回滚(rollback-only)时提前抛出异常标记
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
// 触发事务完成后TransactionSynchronization同步回调
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 触发事务完成后TransactionSynchronization同步回调
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// 当前事务不希望发生回滚但是发生回滚则显式抛出异常
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}
}
回滚事务的执行逻辑与提交事务方法逻辑类似
trigger*
触发事务同步回调rollbackToHeldSavepoint
假如存在保存点(嵌套事务),回滚当前事务到保存点doRollback
回滚事务,DataSourceTransactionManager
实现该抽象模板方法,通过获取数据库连接完成数据库事务回滚cleanupAfterCompletion
事务完成清理操作,比如将数据库连接解绑、恢复旧事务资源,释放数据库连接等
下面是整个事务操作过程的时序图
独立事务与嵌套事务
关于Spring事务传播行为中的 REQUIRES_NEW
与 NESTED
是 Spring 事务传播行为中容易混淆的两个概念,也是在开发过程中经常容易出错的概念点,下面列出了两种行为区别。
特性 | REQUIRES_NEW | NESTED |
---|---|---|
事务独立性 | 完全独立的新事务 | 外层事务的子事务 |
回滚范围 | 仅回滚内层事务 | 回滚到保存点(外层事务继续) |
数据库连接 | 使用新连接 | 复用外层事务连接 |
数据库支持 | 所有数据库 | 需支持保存点(如 MySQL InnoDB) |
适用场景 | 需要完全隔离的操作 | 需要部分回滚的批量操作 |
两者主要的区别是实现机制与事务的影响范围,REQUIRES_NEW
与普通事务实现机制一致,都是依赖于数据库连接的提交或回滚,而NESTED
则需要数据库支持保存点,可提交或回滚到指定保存点。
独立事务与嵌套事务原理
在上文的介绍事务管理器创建事务getTransaction
方法源码中有一段处理已存在事务的逻辑,这里便是处理独立事务与嵌套事务的核心操作
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
Object transaction = doGetTransaction();
// 已有事务处理
if (isExistingTransaction(transaction)) {
// 处理已有事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
...
}
isExistingTransaction
的判断逻辑仅通过当前事务对象中是否存在数据库连接,假如存在则证明当前已有事务。
主要关注处理已有事务方法handleExistingTransaction
,源码如下:
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 当前事务传播行为 PROPAGATION_NEVER 不支持事务,抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 事务传播行为 NOT_SUPPORTED 挂起事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
// 挂起当前事务
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 事务传播行为 REQUIRES_NEW 挂起当前事务开启新事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 挂起当前事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 开启新事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 事务传播行为 PROPAGATION_NESTED 创建保存点
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 使用事务保存点
if (useSavepointForNestedTransaction()) {
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 创建事务保存点
status.createAndHoldSavepoint();
return status;
}
else {
// JTA 事务
return startTransaction(definition, transaction, debugEnabled, null);
}
}
...
// 其他事务传播行为加入当前事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
该方法主要处理当前线程中已存在事务时,根据不同的事务传播行为进行特定操作
- 事务传播行为 NEVER 不支持事务,则抛出异常
- 事务传播行为 NOT_SUPPORTED 非事务,挂起当前事务
- 事务传播行为 REQUIRES_NEW 挂起当前事务,开启新事务
- 事务传播行为 NESTED 创建保存点
- 其他事务行为 则加入当前事务
NESTED
嵌套事务会创建保存点,通过保存点机制实现事务的嵌套处理,而REQUIRES_NEW
独立事务则会挂起事务并创建新事务,假如事务提交或者回滚后则会恢复旧事务,保证了事务间的隔离性。
挂起事务
事务传播行为 REQUIRES_NEW 或者 NOT_SUPPORTED 的事务方法执行时需要独立执行,此时则需要执行挂起(暂停)已有事务,此时会执行 suspend 方法进行处理
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 事务同步器已激活
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 挂起事务同步器
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// 当前存在事务,处理挂起
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;
}
}
protected Object doSuspend(Object transaction) {
// 将数据库连接与线程解绑
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
该方法主要逻辑如下
- 执行事务同步器回调
TransactionSynchronization
的 suspend 回调方法 - 存在事务,直接挂起事务资源,即将数据库连接与线程解绑
- 清空当前事务同步器的线程资源,返回暂停事务资源对象
恢复事务
当内部事务执行结束后需要恢复旧事务资源,提交或回滚事务部分提及了 cleanupAfterCompletion
方法,该方法处理了事务的资源清理回收,包含了旧事务的恢复
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
// 清理事务同步器
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
// 数据库连接与线程解绑
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
// 恢复旧事务
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
查看 resume 方法逻辑
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
// 存在暂停事务资源
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
// 存在旧事务资源,处理恢复
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
// 恢复事务同步器资源
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
// 触发事务恢复回调
doResumeSynchronization(suspendedSynchronizations);
}
}
}
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
// 将旧事务数据库连接与线程绑定
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}
恢复事务就是将挂起事务的相关资源进行恢复
- 将旧事务数据库连接与线程绑定
- 恢复事务同步器资源
- 执行事务同步器回调
TransactionSynchronization
的 resume 回调方法
事务的提交与回滚
REQUIRES_NEW 事务传播行为开启的是一个独立事务,所以与普通事务的提交回滚处理一致,而嵌套事务 NESTED 在提交与回滚时则是进行保存点的提交与回滚操作,以事务的提交代码为例
...
if (status.hasSavepoint()) {
// 嵌套事务释放保存点
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
// 新事务与普通事务方法直接提交
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
...
关于局部回滚标识与全局回滚标识
在上文中的事务提交与回滚内容中提及了 LocalRollbackOnly
和 GlobalRollbackOnly
回滚标记,这两种不同的回滚标记,用于控制事务的回滚行为。
其中局部回滚标记LocalRollbackOnly
表示当前事务(可能是嵌套事务中的一个子事务)需要回滚,但不会直接影响外层事务的提交行为。该标记仅作用于当前事务的本地范围,与事务传播行为PROPAGATION_REQUIRES_NEW
结合可以实现隔离回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
// 其他业务操作
innerMethod()
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
try {
// 业务操作
} catch (Exception e) {
// 标记当前事务为 LocalRollbackOnly
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
在上面的示例中,Spring仅会回滚 innerMethod 方法事务,而外部方法事务不会回滚,从而达到局部事务回滚的操作。
而全局回滚标记GlobalRollbackOnly
表示整个事务链(包括所有外层事务)必须回滚,该标记会从当前事务向外传播,强制外层事务在提交时回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
try {
innerMethod(); // 内层事务标记回滚
// 其他操作(不会执行,因为已触发 GlobalRollbackOnly)
} finally {
// 提交时会回滚
}
}
@Transactional(propagation = Propagation.REQUIRED)
public void innerMethod() {
// 业务失败,标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
上述代码中 innerMethod
与 outerMethod
在同一个事务中REQUIRED
,当内层标记回滚时,整个事务方法都会回滚。
总结:
- 当为独立事务
REQUIRED_NEW
仅回滚当前事务(局部回滚) - 当为嵌套事务
NESTED
仅回滚到保存点,外层事务继续提交 - 当为普通事务时
REQUIRED
强制全局回滚