spring事务之事务挂起和事务恢复源码解读
作者:小小少年_
事务挂起和事务恢复源码解读
在学习spring事务的时候,一定会涉及到一个概念,无法避免的,就是事务挂起和事务恢复
对于事务挂起和事务恢复,可以简单的描述一下,是这样的
- 1.首先我们假设有两个类,A类和B类,两个类中的字段是一模一样的,A类表示当前事务,B类表示备份事务
- 2.如果我开启一个事务,会把当前事务信息,存入到A类中,如果我这时候要进行事务挂起
- 3.事务挂起:就会把A类中当前事务的信息,赋值到B类中,然后在创建一个新事务的时候,会赋值到A类中
- 4.恢复事务:如果此时我当前事务执行完毕了,需要恢复原来的事务,就只需要将A类清空,然后将B类中的数据信息赋值到A类,此时A事务就会再次生效
我觉得可以理解为就是倒腾了一手
事务挂起源码
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
我们直接跳入到这个方法中来看,这个方法是在当前事务存在的时候,会进入到这个方法来处理,执行链路是这样的
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
正常的话,一个事务方法的执行是这个链路,自己debug看下即可,但是要进入到这个方法中,有一个前提,就是当前事务已存在,然后又调用了另外一个事务方法,才会进入到这里
我们以PROPAGATION_REQUIRES_NEW这个级别的传播机制为例,为什么以这个为例,因为这个传播机制,在当前事务存在的时候,是会将当前事务挂起,然后开启一个新的事务,也正好可以看下spring是如何挂起事务,并创建新事务的
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; } }
由于这个方法中,代码比较多,我就删减了一部分,只留下了propagation_requires_new这个传播机制的代码
可以看到,会先调用suspend(transaction)将当前事务挂起,然后再下面的doBegin()再开启一个新的事务,然后通过prepareSynchronization(),将事务相关信息放入到threadLocal中
suspend(transaction)
/** * 这是挂起事务的源码 * 所谓的事务挂起:就是将当前事务管理器中的相关属性,保存到suspendedResourceHolder中 */ @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); } /** * 1.1 下面就是挂起事务的操作,将事务同步管理器中的属性置为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; } }
这是suspend的源码,可以看到,在1.1这个注释位置,会获取到当前事务的属性信息,然后在下面,会new SuspendedResourcesHolder(),将当前事务属性信息放入到这里面
再下面,就是一些异常的判断和处理,我们可以认为,这个方法就是把事务的属性信息存入到了SuspendedResourcesHolder对象中
newTransactionStatus()
这个方法也很重要,会把刚才创建的suspend对象,放入到DefaultTransactionStatus类中,这里我猜是为了在后面事务恢复的时候用的
doBegin()
在doBegin()方法中,主要是重新获取一个数据库连接,然后设置连接的相关信息,比如:非自动提交等
然后将连接信息存入到TransactionSynchronizationManager对象中
我们可以简单认为doBegin()就是重新开启了一个事务连接
事务恢复
前面讲的是事务挂起,下面来说事务恢复,事务恢复,就是在事务提交或者回滚的时候,会进行事务恢复的处理
这里直接贴了一张图,是事务提交和事务回滚的处理流程,最终都会调用到cleanupAfterCompletion()方法,这个方法就是事务恢复的代码
private void cleanupAfterCompletion(DefaultTransactionStatus status) { status.setCompleted(); if (status.isNewSynchronization()) { TransactionSynchronizationManager.clear(); } if (status.isNewTransaction()) { doCleanupAfterCompletion(status.getTransaction()); } if (status.getSuspendedResources() != null) { if (status.isDebug()) { logger.debug("Resuming suspended transaction after completion of inner transaction"); } 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); } } }
这里可以看到,是从resourcesHolder中取一些参数赋值到TransactionSynchronizationManager中;SuspendedResourcesHolder是哪个对象呢?
就是前面事务挂起的时候,将当前事务参数信息赋值到的一个对象
所以
我们可以认为,事务挂起就是将事务赋值到一个临时对象中,事务恢复就是从临时对象中,将事务属性信息赋值到当前事务中
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。