溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring事務(wù)執(zhí)行過程是怎樣的

發(fā)布時間:2022-01-10 09:44:29 來源:億速云 閱讀:98 作者:柒染 欄目:網(wǎng)絡(luò)安全

Spring事務(wù)執(zhí)行過程是怎樣的,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

先說一下啟動過程中的幾個點: 

  加載配置文件:Spring事務(wù)執(zhí)行過程是怎樣的

 Spring事務(wù)執(zhí)行過程是怎樣的

  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ù)源,命名什么的:

Spring事務(wù)執(zhí)行過程是怎樣的

  啟動結(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 :

 Spring事務(wù)執(zhí)行過程是怎樣的

Spring事務(wù)執(zhí)行過程是怎樣的

   determineTransactionManager方法先判斷當(dāng)前事務(wù)有無配置特定事務(wù)管理器,如果沒有判斷是否設(shè)置過默認的事務(wù)管理器,都沒有的情況下:

Spring事務(wù)執(zhí)行過程是怎樣的

    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):

Spring事務(wù)執(zhí)行過程是怎樣的

  判斷是否已設(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);
        }

 Spring事務(wù)執(zhí)行過程是怎樣的

 Spring事務(wù)執(zhí)行過程是怎樣的

  綁定中保留了當(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);
        }
    }

Spring事務(wù)執(zhí)行過程是怎樣的

  沒錯,我們用的是druid,這里不需要解釋了。之后就是一些完成回調(diào),各種解綁、clear、reset了,之前設(shè)置的必須還原為自動提交會在doCleanupAfterCompletion還原,最后關(guān)閉連接。

關(guān)于Spring事務(wù)執(zhí)行過程是怎樣的問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責(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)容。

AI