spring事務(wù)之事務(wù)掛起和事務(wù)恢復(fù)源碼解讀
事務(wù)掛起和事務(wù)恢復(fù)源碼解讀
在學(xué)習(xí)spring事務(wù)的時(shí)候,一定會(huì)涉及到一個(gè)概念,無法避免的,就是事務(wù)掛起和事務(wù)恢復(fù)
對(duì)于事務(wù)掛起和事務(wù)恢復(fù),可以簡(jiǎn)單的描述一下,是這樣的
- 1.首先我們假設(shè)有兩個(gè)類,A類和B類,兩個(gè)類中的字段是一模一樣的,A類表示當(dāng)前事務(wù),B類表示備份事務(wù)
- 2.如果我開啟一個(gè)事務(wù),會(huì)把當(dāng)前事務(wù)信息,存入到A類中,如果我這時(shí)候要進(jìn)行事務(wù)掛起
- 3.事務(wù)掛起:就會(huì)把A類中當(dāng)前事務(wù)的信息,賦值到B類中,然后在創(chuàng)建一個(gè)新事務(wù)的時(shí)候,會(huì)賦值到A類中
- 4.恢復(fù)事務(wù):如果此時(shí)我當(dāng)前事務(wù)執(zhí)行完畢了,需要恢復(fù)原來的事務(wù),就只需要將A類清空,然后將B類中的數(shù)據(jù)信息賦值到A類,此時(shí)A事務(wù)就會(huì)再次生效
我覺得可以理解為就是倒騰了一手
事務(wù)掛起源碼
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
我們直接跳入到這個(gè)方法中來看,這個(gè)方法是在當(dāng)前事務(wù)存在的時(shí)候,會(huì)進(jìn)入到這個(gè)方法來處理,執(zhí)行鏈路是這樣的
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
正常的話,一個(gè)事務(wù)方法的執(zhí)行是這個(gè)鏈路,自己debug看下即可,但是要進(jìn)入到這個(gè)方法中,有一個(gè)前提,就是當(dāng)前事務(wù)已存在,然后又調(diào)用了另外一個(gè)事務(wù)方法,才會(huì)進(jìn)入到這里
我們以PROPAGATION_REQUIRES_NEW這個(gè)級(jí)別的傳播機(jī)制為例,為什么以這個(gè)為例,因?yàn)檫@個(gè)傳播機(jī)制,在當(dāng)前事務(wù)存在的時(shí)候,是會(huì)將當(dāng)前事務(wù)掛起,然后開啟一個(gè)新的事務(wù),也正好可以看下spring是如何掛起事務(wù),并創(chuàng)建新事務(wù)的
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
/**
* 這里是掛起事務(wù)的操作,掛起事務(wù)的話,會(huì)把事務(wù)管理器中的屬性設(shè)置為null
* ,然后將事務(wù)管理器中的屬性暫時(shí)存儲(chǔ)到suspendedResourceHolder中
*/
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啟事務(wù)
doBegin(transaction, definition);
// 將事務(wù)綁定到線程中
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
/**
* 如果在開啟新的事務(wù)的時(shí)候,異常了,就會(huì)在下面這個(gè)方法中,將事務(wù)恢復(fù)(和上面掛起是相對(duì)的)
* ,其實(shí)就是把suspendResourceHolder中的屬性重新賦值到TransactionSynchronizationManager
*/
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}由于這個(gè)方法中,代碼比較多,我就刪減了一部分,只留下了propagation_requires_new這個(gè)傳播機(jī)制的代碼
可以看到,會(huì)先調(diào)用suspend(transaction)將當(dāng)前事務(wù)掛起,然后再下面的doBegin()再開啟一個(gè)新的事務(wù),然后通過prepareSynchronization(),將事務(wù)相關(guān)信息放入到threadLocal中
suspend(transaction)
/**
* 這是掛起事務(wù)的源碼
* 所謂的事務(wù)掛起:就是將當(dāng)前事務(wù)管理器中的相關(guān)屬性,保存到suspendedResourceHolder中
*/
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
/**
* 1.如果當(dāng)前事務(wù)是active狀態(tài),就將事務(wù)掛起,掛起的操作其實(shí)也簡(jiǎn)單
* 將當(dāng)前事務(wù)的屬性信息暫存到SuspendedResourcesHolder中,然后將當(dāng)前事務(wù)的屬性設(shè)置為null
*/
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
/**
* 1.1 下面就是掛起事務(wù)的操作,將事務(wù)同步管理器中的屬性置為null
* , 然后將配置信息,存儲(chǔ)到suspendedResources中,以便在恢復(fù)事務(wù)的時(shí)候,可以恢復(fù)
*/
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.如果掛起事務(wù)失敗,就需要進(jìn)行回滾,就是將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這個(gè)注釋位置,會(huì)獲取到當(dāng)前事務(wù)的屬性信息,然后在下面,會(huì)new SuspendedResourcesHolder(),將當(dāng)前事務(wù)屬性信息放入到這里面
再下面,就是一些異常的判斷和處理,我們可以認(rèn)為,這個(gè)方法就是把事務(wù)的屬性信息存入到了SuspendedResourcesHolder對(duì)象中
newTransactionStatus()
這個(gè)方法也很重要,會(huì)把剛才創(chuàng)建的suspend對(duì)象,放入到DefaultTransactionStatus類中,這里我猜是為了在后面事務(wù)恢復(fù)的時(shí)候用的
doBegin()
在doBegin()方法中,主要是重新獲取一個(gè)數(shù)據(jù)庫(kù)連接,然后設(shè)置連接的相關(guān)信息,比如:非自動(dòng)提交等
然后將連接信息存入到TransactionSynchronizationManager對(duì)象中
我們可以簡(jiǎn)單認(rèn)為doBegin()就是重新開啟了一個(gè)事務(wù)連接
事務(wù)恢復(fù)
前面講的是事務(wù)掛起,下面來說事務(wù)恢復(fù),事務(wù)恢復(fù),就是在事務(wù)提交或者回滾的時(shí)候,會(huì)進(jìn)行事務(wù)恢復(fù)的處理

這里直接貼了一張圖,是事務(wù)提交和事務(wù)回滾的處理流程,最終都會(huì)調(diào)用到cleanupAfterCompletion()方法,這個(gè)方法就是事務(wù)恢復(fù)的代碼
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());
}
}
在這個(gè)代碼中,前面是一些邏輯處理,應(yīng)該是對(duì)當(dāng)前事務(wù)進(jìn)行清除的操作,需要關(guān)注的是最后一行代碼,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中取一些參數(shù)賦值到TransactionSynchronizationManager中;SuspendedResourcesHolder是哪個(gè)對(duì)象呢?
就是前面事務(wù)掛起的時(shí)候,將當(dāng)前事務(wù)參數(shù)信息賦值到的一個(gè)對(duì)象
所以
我們可以認(rèn)為,事務(wù)掛起就是將事務(wù)賦值到一個(gè)臨時(shí)對(duì)象中,事務(wù)恢復(fù)就是從臨時(shí)對(duì)象中,將事務(wù)屬性信息賦值到當(dāng)前事務(wù)中
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Security動(dòng)態(tài)權(quán)限的實(shí)現(xiàn)方法詳解
這篇文章主要和小伙伴們簡(jiǎn)單介紹下 Spring Security 中的動(dòng)態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項(xiàng)目中的權(quán)限方案,感興趣的可以了解一下2022-06-06
Springboot應(yīng)用gradle?Plugin示例詳解
這篇文章主要介紹了Springboot應(yīng)用gradle?Plugin詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
springboot中關(guān)于自動(dòng)建表,無法更新字段的問題
這篇文章主要介紹了springboot中關(guān)于自動(dòng)建表,無法更新字段的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
springboot項(xiàng)目部署到寶塔的詳細(xì)圖文教程
網(wǎng)上關(guān)于寶塔運(yùn)行springBoot的東西說有點(diǎn)迷糊,但是有一句話很重要,Spring boot項(xiàng)目只需要JDK環(huán)境即可部署成功,下面這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目部署到寶塔的詳細(xì)圖文教程,需要的朋友可以參考下2023-05-05

