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還原,最后關閉連接。

  過程就是這樣了,其實我想實驗個想法,於是看了這些代碼。以上。

org/springframework/transaction/interceptor/TransactionAspectSupport --> createTransactionIfNecessary -->

/org/springframework/transaction/support/AbstractPlatformTransactionManager --> status = tm.getTransaction(txAttr) --> doBegin

 

createTransactionIfNecessary --> {doCleanupAfterCompletion commitTransactionAfterReturning}

--> org/springframework/transaction/support/AbstractPlatformTransactionManager

--> commit --> processCommit --> {prepareForCommit, doCommit, triggerAfterCommit triggerAfterCompletion cleanupAfterCompletion}

 

 

 

doCommit --> DataSourceTransactionManager

cleanupAfterCompletion --> DataSourceTransactionManager --> doCleanupAfterCompletion

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公眾號:

                      

轉載請注明出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM