这篇笔记主要记录,在spring事务源码中,传播机制的学习
传播机制枚举值
这是在网上扒的一张图,主要是记录事务传播机制的类型
接下来记录源码的处理逻辑
源码
事务传播机制的处理
传播机制的源码,我们从这个方法开始看起:
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
对于事务传播机制的行为,无非就是两种场景:当前存在事务,当前不存在事务,所以,spring源码中,也是根据这两种场景做了不同的处理
/**
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*
* 在这个方法中,有两个比较重要的方法:分别是suspend()和doBegin()
* suspend()是将当前事务挂起,所谓的挂起就是事务中的属性信息暂时存到SuspendTransactionResource
* doBegin()
* 是开启一个事务,所谓的开启事务,就是获取一个数据库连接,然后将连接信息绑定到DataSourceTransactionObject上
*/
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
/**
* TODO 这里是获取到当前的事务对象,这里待学习
* 1、这里是new一个新的DataSourceTransactionObject对象
* 在后面开启事务 doBegin()方法中,会对txObject对象进行填充
*/
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
/**
* 2、如果当前存在事务,就根据不同的传播机制来处理
* PROPAGATION_NOT_SUPPORTED:以非事务方法运行,如果存在事务,就把当前事务挂起
* PROPAGATION_REQUIRES_NEW:新建事务,如果存在事务,就把当前事务挂起
* PROPAGATION_NESTED:如果当前事务存在,就嵌套事务运行,如果事务不存在,就创建新的事务
* PROPAGATION_NEVER:以非事务运行,如果当前存在事务,就抛出异常
*
*
* 如果没有进入这个if判断,就表示当前不存在事务
* PROPAGATION_MANDATORY:使用当前事务运行,如果不存在事务,就抛出异常
* PROPAGATION_REQUIRED:如果不存在事务,就新建一个事务运行;如果事务存在,就使用当前事务
* PROPAGATION_REQUIRES_NEW:如果当前事务存在,就新建一个事务,将原来的事务挂起
* PROPAGATION_NESTED:
*
*
* PROPAGATION_SUPPORTS:没有进行判断处理,因为这个隔离级别是:如果事务存在,就事务运行,如果事务不存在,就非事务运行
*/
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
/**
* 3、判断超时时间是否小于-1
*/
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
/**
* 4、代码走到这里,理论上应该是不存在事务,才会走到这里
* 当事务不存在的时候,就判断当前配置的事务传播机制是什么,mandatory必须以事务方式运行,没有事务就报错
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
/**
* 5、required、required_new、nested这三种情况,在没有事务的情况下,需要开启新事务,所以进行统一的处理即可
*/
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
/**
* 5.1、suspend是挂起线程的意思,这里由于不存在线程,所以传null即可
*/
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
/**
* 5.2、开启一个新的事务,这个方法十分重要
*/
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
/**
* 6、代码执行到这里,说明当前不存在事务,且事务的传播机制也不是上面四种
* PROPAGATION_REQUIRED
* PROPAGATION_REQUIRES_NEW
* PROPAGATION_NESTED
* PROPAGATION_MANDATORY
*
*
* 剩下的三种都是在非事务方法中运行、或者是没有事务,就以非事务方法运行的
* support
* not_support
* never
*/
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
在这个方法中,大致就是这么几个步骤
1、获取当前事务,这里获取当前事务和后面开启事务有关系,并且用到了threadLocal
2、然后根据当前是否存在事务来判断要进行不同的传播机制的判断
1、获取事务
@Override
protected Object doGetTransaction() {
// 1.创建一个数据源事务对象,将当前事务放入到txObject中
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
/**
* 2.设置是否允许嵌套事务
* 在初始化DataSourceManager的时候会执行为true
*/
txObject.setSavepointAllowed(isNestedTransactionAllowed());
/**
* 3.从TransactionSynchronizationManager
* 中获取到对应的数据库连接信息,第一次理论上是拿不到对象的,因为是在购买doBegin()
* 方法中调用TransactionSynchronizationManager的bindResource()方法的
*/
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
这个方法就是把已经开启的事务放入到txObject中,如果是 第一次进入到这个方法中,此时是不存在事务的
2、判断是否存在事务
/**
* 这里是判断是否存在事务,这个txObject只有在开启一个事务的时候,才会将connection以及事务信息存入到该对象中
* 所以:判断当前是否存在事务,就只需要判断connectionHolder是否非null,且active为true
* @param transaction transaction object returned by doGetTransaction
* @return
*/
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
这里判断是否存在事务的时候,就是判断第一点中,放入到txObject对象中的connectionHolder是否为null,且active为true;如果同时满足这两个条件,我们就认为当前存在事务,否则不存在事务
3、存在事务
/**
* Create a TransactionStatus for an existing transaction.
* PROPAGATION_NEVER
* PROPAGATION_NOT_SUPPORTED
* PROPAGATION_REQUIRES_NEW
* PROPAGATION_NESTED
* 进入到这个代码的前提是当前存在事务
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
/**
* 1、方法运行到这里,说明当前是有事务的
* PROPAGATION_NEVER:是以非事务运行,如果存在事务,就报错
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
/**
* 2、以非事务方式运行,如果当前存在事务,就将事务挂起,然后以非事务方式运行
* 这里牵扯到一个事务挂起的操作
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
/**
* 3、新建事务,如果当前存在事务,就将事务挂起
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
/**
* 这里是挂起事务的操作,挂起事务的话,会把事务管理器中的属性设置为null
* ,然后将事务管理器中的属性暂时存储到suspendedResourceHolder中
*/
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
/**
* 如果在开启新的事务的时候,异常了,就会在下面这个方法中,将事务恢复(和上面挂起是相对的)
* ,其实就是把suspendResourceHolder中的属性重新赋值到TransactionSynchronizationManager
*/
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
/**
* 4、如果事务存在,就在嵌套事务内运行,如果事务不存在,就创建一个新的事务
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 判断是否允许嵌套事务运行
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
//创建一个新的事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
/**
* 代码如果执行到这里,说明当前有事务,且传播机制不是上面这几个
* PROPAGATION_NESTED
* PROPAGATION_REQUIRES_NEW
* PROPAGATION_NEVER
* PROPAGATION_NOT_SUPPORTED
*
* 剩下的三个
* required
* support
* MANDATORY
* 都是必须以事务运行的
*
*/
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
这是存在事务的处理方法,主要的分支代码就是以下几个
1、如果是PROPAGATION_NEVER,就直接抛出异常
2、如果是PROPAGATION_NOT_SUPPORTED,就将当前事务挂起,然后以非事务方法运行
3、如果是PROPAGATION_REQUIRES_NEW,就将事务挂起,然后开启一个新的事务
4、如果是PROPAGATION_NESTED,如果允许嵌套事务运行,就嵌套事务运行,如果不允许,就创建一个新的事务
5、其他传播机制,就在当前事务方法中运行
4、不存在事务
上面源码中
事务传播机制的处理
这里的源码中,第三点开始,是对不存在事务的场景进行的处理。需要注意的是,下面的这个逻辑是在不存在事务的前提下
1、如果是PROPAGATION_MANDATORY,就抛出异常,因为这个传播机制必须以事务方法运行
2、如果是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED这三个,就需要开启新的事务
3、support、not_support、never这三个,是以非事务运行的,所以无需做其他处理
5、开启事务
在上面3和4的这个过程中,需要涉及到两个概念,分别是事务挂起和事务开启
/**
* This implementation sets the isolation level but ignores the timeout.
*
* 这里是开启事务的方法,主要完成了以下的操作
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
/**
* 1.获取一个数据库连接
*/
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
/**
* 2.然后将连接存入到txObject中;这里的txObject在前面是否存在事务的时候,会用到
* isExistTransaction()方法
*/
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
/**
* 设置事务的隔离级别?
*/
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
/**
* 3.设置为非自动提交
*/
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
/**
* 4.设置事务为只读 set transaction read only
* 然后设置事务为active状态
* 最重要的是:需要将事务信息绑定到TransactionSynchronizationManager中
*/
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
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);
}
}
这里最主要的几个操作:
1、获取一个connection,然后将自动提交设置为false
2、设置事务的隔离级别
3、将transactionActive属性设置为true
4、将事务信息绑定到TransactionSynchronizationManager中,在其内部,是通过threadLocal来完成的
这里的3和4就和前面判断是否存在事务相关联起来了
可以看到在这个方法中所谓的绑定,就是通过bindResource来完成的,在前面第一点获取事务的时候,是通过getResource来获取的
所以:开启事务,就是获取一个connection,然后将其事务绑定到TransactionSynchronizationManager中
6、挂起事务
在事务挂起和开启事务这两个操作中,有两个关键的类:
TransactionSynchronizationManager和SuspendedResourcesHolder
我的理解是:TransactionSynchronizationManager中存储的是事务的相关信息,SuspendedResourcesHolder是用来存放挂起事务的相关信息,所以,所谓的挂起事务,就是把事务信息从一个类中TransactionSynchronizationManager,存入到了SuspendedResourcesHolder中,这样,当前事务就为空,就可以接着开启新的事务,然后在恢复事务的时候,将SuspendedResourcesHolder中暂存的事务信息恢复到TransactionSynchronizationManager中
这就是我对挂起事务和恢复事务的理解,在事务提交或者异常回滚的时候,都会调用恢复事务的方法
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
/**
* 1.如果当前事务是active状态,就将事务挂起,挂起的操作其实也简单
* 将当前事务的属性信息暂存到SuspendedResourcesHolder中,然后将当前事务的属性设置为null
*/
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
/**
* 下面就是挂起事务的操作,将事务同步管理器中的属性置为null
* ,然后将配置信息,存储到suspendedResources中,以便在恢复事务的时候,可以恢复
*/
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) {
/**
* 2.如果挂起事务失败,就需要进行回滚,就是将suspendedResourcesHolder
* 中的属性重新赋值到TransactionSynchronizationManager中
*/
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}