您好,登錄后才能下訂單哦!
Spring事務(wù)執(zhí)行過程是怎樣的,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
先說一下啟動過程中的幾個點:
加載配置文件:
AbstractAutowireCapableBeanFactory.doCreateBean --> initializeBean --> applyBeanPostProcessorsAfterInitialization --> beanProcessor.postProcessAfterInitialization --> AbstractAutoProxyCreator.postProcessAfterInitialization --> wrapIfNecessary(bean, beanName, cacheKey) --> getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是將Advice注入程序中Pointcut位置的代碼;AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary:調(diào)用txAdvice上圖的事務(wù)通知設(shè)置數(shù)據(jù)源 DataSourceTransactionManager,ChooseDataSource是項目中配置的自定義的繼承至AbstractRoutingDataSource的默認數(shù)據(jù)源,命名什么的:
啟動結(jié)束后,發(fā)起事務(wù)調(diào)用,首先攔截方法起CglibAopProxy.intercept --> ReflectiveMethodInvocation.proceed --> ExposeInvocationInterceptor.invoke --> TransactionInterceptor.invoke:
public Object invoke(final MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() { @Override public Object proceedWithInvocation() throws Throwable { return invocation.proceed(); } }); }
TransactionAspectSupport.invokeWithinTransaction :
determineTransactionManager方法先判斷當(dāng)前事務(wù)有無配置特定事務(wù)管理器,如果沒有判斷是否設(shè)置過默認的事務(wù)管理器,都沒有的情況下:
public PlatformTransactionManager getTransactionManager() { return this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); }
接下來判斷加載的事務(wù)屬性是否存在或者當(dāng)前事務(wù)管理器是否不是CallbackPreferringPlatformTransactionManager,符合條件會執(zhí)行到createTransactionIfNecessary,先是:
if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName() { return joinpointIdentification; } }; }
DelegatingTransactionAttribute本身除了被實現(xiàn)后使用沒有其他作用。然后從事務(wù)管理器中取出事務(wù),doGetTransaction從之前set的數(shù)據(jù)源取出連接set給DataSourceTransactionObject:
protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource); txObject.setConnectionHolder(conHolder, false); return txObject; }
然后判斷事務(wù)是否已存在,如果存在例如嵌套事務(wù),會根據(jù)定義的傳播方式進行處理,具體處理后面會說,這里是還不存在。然后驗證了一下事務(wù)是否超時。由于從事務(wù)定義(TransactionDefinition持有隔離級別等事務(wù)屬性的對象)中取出的事務(wù)傳播方式我這里是默認的 PROPAGATION_REQUIRED,第一句用于掛起事務(wù)的什么也沒做,然后或許事務(wù)同步為激活同步,接著就到啟動事務(wù)了DataSourceTransactionManager doBegin:
if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); }
先從數(shù)據(jù)源中g(shù)etConnection,set給DataSourceTransactionObject,設(shè)置為同步事務(wù),prepareConnectionForTransaction根據(jù)配置確定事務(wù)是否只讀,被嵌套的事務(wù)中前一個的隔離級別txObject.setPreviousIsolationLevel(previousIsolationLevel):
判斷是否已設(shè)置為自動提交,如果是設(shè)置則設(shè)置事務(wù)對象為自動還原為自動提交,有點拗口,意思大概是當(dāng)事務(wù)復(fù)用數(shù)據(jù)庫連接時第一個事務(wù)提交后,同一個連接的下一個事務(wù)還是設(shè)置為自動提交,否則當(dāng)前事務(wù)如果被設(shè)為手痛提交,因為連接池中的連接會被復(fù)用,在同一個連接上的后續(xù)事務(wù)可能需要手動調(diào)用conn.commit才能提交下一個事務(wù),設(shè)置connection holder代表的連接的事務(wù)是活動的;檢查超時;判斷當(dāng)前事務(wù)的連接是否是新創(chuàng)建的,是則注冊給TransactionSynchronizationManager,通過ThreadLocal將線程和事務(wù)綁定;prepareSynchronization設(shè)置這個事務(wù)同步管理器是否包含實際執(zhí)行的事務(wù),當(dāng)前線程事務(wù)隔離級別、是否只讀以及事務(wù)定義名,初始化同步事務(wù)(private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations")):
public static void initSynchronization() throws IllegalStateException { if (isSynchronizationActive()) { throw new IllegalStateException("Cannot activate transaction synchronization - already active"); } logger.trace("Initializing transaction synchronization"); synchronizations.set(new LinkedHashSet<TransactionSynchronization>()); }
回到TransactionAspectSupport,prepareTransactionInfo方法創(chuàng)建事務(wù)信息,并通過ThreadLocal綁定給當(dāng)前線程:
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
private void bindToThread() { // Expose current TransactionStatus, preserving any existing TransactionStatus // for restoration after this transaction is complete. this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); }
綁定中保留了當(dāng)前事務(wù)狀態(tài),應(yīng)該用來在嵌套事務(wù)中內(nèi)層事務(wù)完成后恢復(fù)外層事務(wù)的現(xiàn)場。
createTransactionIfNecessary結(jié)束,回到TransactionAspectSupport的invokeWithinTransaction方法,接下來是invocation.proceedWithInvocation,這個方法上面貼了,new InvocationCallback時實現(xiàn)了這個方法,代碼只有一句調(diào)用了MethodInvocation的proceed,也就是環(huán)繞通知調(diào)用被切方法的方法(也有可能調(diào)用其他Interceptor的切面方法),我這直接調(diào)用了被切的方法,然而并沒有直接走到后面的completeTransactionAfterThrowing或commitTransactionAfterReturning,也沒有到清理事務(wù)信息的方法,因為有嵌套事務(wù),于是被嵌套的切了,與上面過程相同之處就不說了,說說不同的。
getTransaction這次因為已經(jīng)存在事務(wù),使用同一個連接,JdbcTransactionObjectSupport實例保存了connectionHolder,所以這次走進了這個判斷分支:
if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); }
handleExistingTransaction方法內(nèi)根據(jù)不同的事務(wù)傳播方式走不同的代碼分支,我這就是默認REQUIRED使用現(xiàn)有事務(wù),所以這個方法基本和沒走也差不多。嵌套的都完了就走到cleanupTransactionInfo方法了,這方法實際調(diào)用了TransactionInfo.restoreThreadLocalStatus,實際上還原了之前的事務(wù)信息:
private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); }
然后是commitTransactionAfterReturning:
/** * Execute after successful completion of call, but not after an exception was handled. * Do nothing if we didn't create a transaction. * @param txInfo information about the current transaction */ protected void commitTransactionAfterReturning(TransactionInfo txInfo) { if (txInfo != null && txInfo.hasTransaction()) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
commit方法在AbstractPlatformTransactionManager中,先提交的是內(nèi)層的事務(wù),這里需要提的,嵌套事務(wù)的子事務(wù)報錯但沒有拋給外層事務(wù),可能會出現(xiàn)rollback-only的問題,defStatus.isLocalRollbackOnly()就是判斷是否有內(nèi)層事務(wù)出錯設(shè)置rollbackOnly為true了,另外,關(guān)于全局事務(wù),似乎說的是用的兩段式XA?,不過目前用不上,只是一個連接對數(shù)據(jù)庫,這里可以考慮下。該到具體處理提交的方法processCommit了。同樣在AbstractPlatformTransactionManager中。prepareForCommit方法是空的,protected應(yīng)該是準備給子類重寫的,或者這就是我要找的。savepoint沒有,由于是內(nèi)層事務(wù),triggerBeforeCommit、triggerBeforeCompletion、triggerAfterCommit和triggerAfterCommit方法沒有執(zhí)行,設(shè)置事務(wù)狀態(tài)后,這個內(nèi)層事務(wù)就提交完了。
ExposeInvocationInterceptor(可以暴露出攔截器鏈,一般用不到它,用到時應(yīng)該在鏈首)還原外層被攔截方法的執(zhí)行:
@Override public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } }
這個外層就是實際被攔截的方法,會通過CglibAopProxy執(zhí)行。
再來就是提交外層事務(wù)了,cleanupTransactionInfo的old這次是null了,一樣的流程就不說了,由于外層事務(wù)是創(chuàng)建了同步對象所以triggerBeforeCommit執(zhí)行了:
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
public static void triggerBeforeCommit(boolean readOnly) { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.beforeCommit(readOnly); } }
這里的beforeCommit由于我用的是mybatis,所以如果執(zhí)行的話,會執(zhí)行到SqlSessionUtils.beforeCommit,然而由于SqlSessionUtils是不能被繼承的,所以這里不太好動手腳,只能在這個類的外層想辦法。triggerBeforeCompletion是類似的。外層事務(wù)會有獲取status.isGlobalRollbackOnly()用于doCommit(status)之后是否報錯,注意,就是說其實并不會打斷提交的執(zhí)行。doCommit(status):
@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 { con.commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit JDBC transaction", ex); } }
沒錯,我們用的是druid,這里不需要解釋了。之后就是一些完成回調(diào),各種解綁、clear、reset了,之前設(shè)置的必須還原為自動提交會在doCleanupAfterCompletion還原,最后關(guān)閉連接。
關(guān)于Spring事務(wù)執(zhí)行過程是怎樣的問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。