您好,登錄后才能下訂單哦!
Spring事務執行過程是怎樣的,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
先說一下啟動過程中的幾個點:
加載配置文件:
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:調用txAdvice上圖的事務通知設置數據源 DataSourceTransactionManager,ChooseDataSource是項目中配置的自定義的繼承至AbstractRoutingDataSource的默認數據源,命名什么的:
啟動結束后,發起事務調用,首先攔截方法起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方法先判斷當前事務有無配置特定事務管理器,如果沒有判斷是否設置過默認的事務管理器,都沒有的情況下:
public PlatformTransactionManager getTransactionManager() { return this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); }
接下來判斷加載的事務屬性是否存在或者當前事務管理器是否不是CallbackPreferringPlatformTransactionManager,符合條件會執行到createTransactionIfNecessary,先是:
if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName() { return joinpointIdentification; } }; }
DelegatingTransactionAttribute本身除了被實現后使用沒有其他作用。然后從事務管理器中取出事務,doGetTransaction從之前set的數據源取出連接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; }
然后判斷事務是否已存在,如果存在例如嵌套事務,會根據定義的傳播方式進行處理,具體處理后面會說,這里是還不存在。然后驗證了一下事務是否超時。由于從事務定義(TransactionDefinition持有隔離級別等事務屬性的對象)中取出的事務傳播方式我這里是默認的 PROPAGATION_REQUIRED,第一句用于掛起事務的什么也沒做,然后或許事務同步為激活同步,接著就到啟動事務了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); }
先從數據源中getConnection,set給DataSourceTransactionObject,設置為同步事務,prepareConnectionForTransaction根據配置確定事務是否只讀,被嵌套的事務中前一個的隔離級別txObject.setPreviousIsolationLevel(previousIsolationLevel):
判斷是否已設置為自動提交,如果是設置則設置事務對象為自動還原為自動提交,有點拗口,意思大概是當事務復用數據庫連接時第一個事務提交后,同一個連接的下一個事務還是設置為自動提交,否則當前事務如果被設為手痛提交,因為連接池中的連接會被復用,在同一個連接上的后續事務可能需要手動調用conn.commit才能提交下一個事務,設置connection holder代表的連接的事務是活動的;檢查超時;判斷當前事務的連接是否是新創建的,是則注冊給TransactionSynchronizationManager,通過ThreadLocal將線程和事務綁定;prepareSynchronization設置這個事務同步管理器是否包含實際執行的事務,當前線程事務隔離級別、是否只讀以及事務定義名,初始化同步事務(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方法創建事務信息,并通過ThreadLocal綁定給當前線程:
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); }
綁定中保留了當前事務狀態,應該用來在嵌套事務中內層事務完成后恢復外層事務的現場。
createTransactionIfNecessary結束,回到TransactionAspectSupport的invokeWithinTransaction方法,接下來是invocation.proceedWithInvocation,這個方法上面貼了,new InvocationCallback時實現了這個方法,代碼只有一句調用了MethodInvocation的proceed,也就是環繞通知調用被切方法的方法(也有可能調用其他Interceptor的切面方法),我這直接調用了被切的方法,然而并沒有直接走到后面的completeTransactionAfterThrowing或commitTransactionAfterReturning,也沒有到清理事務信息的方法,因為有嵌套事務,于是被嵌套的切了,與上面過程相同之處就不說了,說說不同的。
getTransaction這次因為已經存在事務,使用同一個連接,JdbcTransactionObjectSupport實例保存了connectionHolder,所以這次走進了這個判斷分支:
if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); }
handleExistingTransaction方法內根據不同的事務傳播方式走不同的代碼分支,我這就是默認REQUIRED使用現有事務,所以這個方法基本和沒走也差不多。嵌套的都完了就走到cleanupTransactionInfo方法了,這方法實際調用了TransactionInfo.restoreThreadLocalStatus,實際上還原了之前的事務信息:
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中,先提交的是內層的事務,這里需要提的,嵌套事務的子事務報錯但沒有拋給外層事務,可能會出現rollback-only的問題,defStatus.isLocalRollbackOnly()就是判斷是否有內層事務出錯設置rollbackOnly為true了,另外,關于全局事務,似乎說的是用的兩段式XA?,不過目前用不上,只是一個連接對數據庫,這里可以考慮下。該到具體處理提交的方法processCommit了。同樣在AbstractPlatformTransactionManager中。prepareForCommit方法是空的,protected應該是準備給子類重寫的,或者這就是我要找的。savepoint沒有,由于是內層事務,triggerBeforeCommit、triggerBeforeCompletion、triggerAfterCommit和triggerAfterCommit方法沒有執行,設置事務狀態后,這個內層事務就提交完了。
ExposeInvocationInterceptor(可以暴露出攔截器鏈,一般用不到它,用到時應該在鏈首)還原外層被攔截方法的執行:
@Override public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } }
這個外層就是實際被攔截的方法,會通過CglibAopProxy執行。
再來就是提交外層事務了,cleanupTransactionInfo的old這次是null了,一樣的流程就不說了,由于外層事務是創建了同步對象所以triggerBeforeCommit執行了:
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
public static void triggerBeforeCommit(boolean readOnly) { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.beforeCommit(readOnly); } }
這里的beforeCommit由于我用的是mybatis,所以如果執行的話,會執行到SqlSessionUtils.beforeCommit,然而由于SqlSessionUtils是不能被繼承的,所以這里不太好動手腳,只能在這個類的外層想辦法。triggerBeforeCompletion是類似的。外層事務會有獲取status.isGlobalRollbackOnly()用于doCommit(status)之后是否報錯,注意,就是說其實并不會打斷提交的執行。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,這里不需要解釋了。之后就是一些完成回調,各種解綁、clear、reset了,之前設置的必須還原為自動提交會在doCleanupAfterCompletion還原,最后關閉連接。
關于Spring事務執行過程是怎樣的問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。