Spring事務(wù)處理機(jī)制以及錯(cuò)誤使用TransactionSynchronization的afterCompletion方法引起的問(wèn)題

前言

我們都知道spring有聲明式事務(wù)和編程式事務(wù)淫半,聲明式只需要提供@Transactional的注解镇眷,然后事務(wù)的開啟和提交/回滾、資源的清理就都由spring來(lái)管控戒努,我們只需要關(guān)注業(yè)務(wù)代碼即可剔交;而編程式事務(wù)則需要使用spring提供的模板肆饶,如TransactionTemplate,或者直接使用底層的PlatformTransactionManager岖常。

聲明式事務(wù)的最大優(yōu)點(diǎn)就是對(duì)代碼的侵入性較小驯镊,只需要在方法上加@Transactional的注解就可以實(shí)現(xiàn)事務(wù);編程式事務(wù)的最大優(yōu)點(diǎn)就是事務(wù)的管控粒度較細(xì)竭鞍,在實(shí)現(xiàn)某個(gè)代碼塊的事務(wù)阿宅。

背景

簡(jiǎn)單介紹完spring的事務(wù)機(jī)制那就要引入這一次碰到的問(wèn)題了,我相信大多數(shù)人應(yīng)該和我一樣笼蛛,只要怎么使用洒放,比如加個(gè)注解啥的,但是底層原理不清楚滨砍。好一點(diǎn)的知道AOP動(dòng)態(tài)代理往湿,再好一點(diǎn)就是知道事務(wù)的傳播機(jī)制(一般也就用用默認(rèn)的REQUIRED)。真正底層的事務(wù)處理的源碼很多人應(yīng)該是沒(méi)有看過(guò)的惋戏,當(dāng)然我也是沒(méi)有滴~~ 但是這一次碰到的問(wèn)題讓我不得不去看源碼了领追。

這段時(shí)間一直在做代碼的重構(gòu),既然是重新寫的代碼响逢,當(dāng)然想寫得漂亮一點(diǎn)绒窑,不然是要被后人戳脊梁骨的~~ 結(jié)果所有代碼都寫完,都提測(cè)了舔亭,在測(cè)試環(huán)境卻報(bào)一個(gè)詭異的異常

java.sql.SQLException: PooledConnection has already been closed

而且這不是必現(xiàn)的些膨,而一旦出現(xiàn),那任何涉及數(shù)據(jù)庫(kù)連接的接口都有可能報(bào)這個(gè)錯(cuò)钦铺。從字面意思看是用到的數(shù)據(jù)庫(kù)連接被關(guān)閉了订雾,但是理解不能啊,這種底層的事情不都是spring幫忙做好了么矛洞。建議測(cè)試重啟機(jī)器洼哎,心中期待不會(huì)再現(xiàn)

結(jié)果依然出現(xiàn),而且頻率還不低,都阻塞測(cè)試了噩峦。锭沟。那就只好操起久違的調(diào)試源碼的大刀,硬著頭皮上了识补。

過(guò)程

本次源碼使用的是spring版本是 4.2.4.RELEASE族淮,事務(wù)管理器則是參照項(xiàng)目使用的DataSourceTransactionManager。

入口

  1. 首先是事務(wù)的入口李请,spring用的是動(dòng)態(tài)代理,如果某個(gè)方法被標(biāo)注了@Transactional厉熟,則會(huì)由TransactionInterceptor攔截导盅,在原始方法的前后增加一些額外的處理∽嵘可以看到白翻,調(diào)用的是TransactionInterceptor的invoke方法,而內(nèi)部又調(diào)用了invokeWithinTransaction方法绢片,但其實(shí)這個(gè)并不一定會(huì)創(chuàng)建事務(wù)(事務(wù)傳播機(jī)制里有幾種情況是不需要或者不支持事務(wù)的)滤馍。
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();
        }
    });
}
  1. TransactionInterceptor繼承了TransactionAspectSupport這個(gè)抽象類,invokeWithinTransaction這個(gè)方法是在父類中的底循。方法里的英文注釋是源碼中的巢株,中文注釋是我加上去的。CallbackPreferringPlatformTransactionManager是實(shí)現(xiàn)了PlatformTransactionManager接口熙涤,如果使用的事務(wù)管理器是CallbackPreferringPlatformTransactionManager的實(shí)現(xiàn)阁苞,則會(huì)將事務(wù)的控制交由這個(gè)類的execute方法,這里先省略祠挫。我們來(lái)看一般情況(很多應(yīng)該用的是DataSourceTransactionManager吧)那槽,總的來(lái)說(shuō)可以將這個(gè)方法分為幾部分:
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 如果當(dāng)前方法需要事務(wù)則會(huì)創(chuàng)建事務(wù)(@Transactional不代表一定創(chuàng)建事務(wù),可以看spring的事務(wù)傳播機(jī)制)
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 可以將這個(gè)方法視為調(diào)用真正的業(yè)務(wù)方法(其實(shí)內(nèi)部還有一些攔截器的處理)
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 事務(wù)拋出異常的時(shí)候的處理
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 只做一件事等舔,就是把事務(wù)的上下文信息改回本事務(wù)開始之前的上下文
            // 因?yàn)橛锌赡鼙臼聞?wù)是被包裹在其他事務(wù)中的骚灸,可以看spring的事務(wù)傳播機(jī)制
            cleanupTransactionInfo(txInfo);
        }
        // 事務(wù)執(zhí)行成功后將事務(wù)的狀態(tài)信息提交
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        // 省略
    }
}

createTransactionIfNecessary

這里要知道幾個(gè)類的含義:

  • TransactionAttribute 事務(wù)的屬性,比如我們?cè)贎Transactional里面的一些定義慌植,使用的事務(wù)管理器甚牲、事務(wù)隔離級(jí)別、超時(shí)時(shí)間等
  • TransactionStatus 事務(wù)的運(yùn)行時(shí)狀態(tài)蝶柿,如是否已完成等
  • TransactionInfo 事務(wù)信息的聚合鳖藕,包含了事務(wù)屬性、事務(wù)狀態(tài)只锭、事務(wù)管理器著恩、被事務(wù)包裹的方法定義信息、本事務(wù)執(zhí)行前的外層事務(wù)信息等

這里復(fù)雜的是獲取事務(wù)的方法,其他方法做的事情見(jiàn)我的中文注釋喉誊。

protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
    // If no name specified, apply method identification as transaction name.
    // 新建一個(gè)TransactionAttribute的代理對(duì)象邀摆,其實(shí)用的是裝飾器模式
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 這里會(huì)根據(jù)事務(wù)管理器獲取事務(wù)對(duì)象
            status = tm.getTransaction(txAttr);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured");
            }
        }
    }
    // 將事務(wù)信息聚合然后返回,這里會(huì)有一個(gè)事務(wù)信息綁定到當(dāng)前線程的操作(外層事務(wù)信息會(huì)存下來(lái))
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
  1. getTransaction方法
  • TransactionSynchronizationManager 以threadLocal的方式保存當(dāng)前線程事務(wù)相關(guān)信息的對(duì)象

這里省略了一些不重要的流程伍茄,重點(diǎn)是doBegin方法栋盹,這里會(huì)開啟事務(wù)

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    // 會(huì)新建一個(gè)事務(wù)對(duì)象,從TransactionSynchronizationManager中獲取當(dāng)前線程持有的數(shù)據(jù)庫(kù)連接的句柄
    //如果是最開始的事務(wù)敷矫,這個(gè)句柄是會(huì)為null的例获,如果是內(nèi)層事務(wù),則會(huì)復(fù)用連接
    Object transaction = doGetTransaction();

    // 省略
    
    // 當(dāng)前線程關(guān)聯(lián)的數(shù)據(jù)庫(kù)連接存在且事務(wù)處于激活狀態(tài)曹仗,那么當(dāng)前事務(wù)會(huì)根據(jù)事務(wù)傳播機(jī)制來(lái)處理當(dāng)前事務(wù)
    if (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    // 省略

    // No existing transaction found -> check propagation behavior to find out how to proceed.
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        // 省略
    }
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 開啟事務(wù)
            doBegin(transaction, definition);
            // 將事務(wù)信息綁定到當(dāng)前線程(存在TransactionSynchronizationManager的threadLocal中)
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException ex) {
            resume(null, suspendedResources);
            throw ex;
        }
        catch (Error err) {
            resume(null, suspendedResources);
            throw err;
        }
    }
    else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        // 這里就是前面說(shuō)的不需要事務(wù)的情況
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                    "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
}
  1. doBegin方法
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;

    try {
        if (txObject.getConnectionHolder() == null ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            // 從dataSource從獲取一個(gè)連接榨汤,如果使用了連接池,則會(huì)從連接池中獲取
            Connection newCon = this.dataSource.getConnection();
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
            // 事務(wù)對(duì)象設(shè)置連接的句柄
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();

        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);

        // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
        // so we don't want to do it unnecessarily (for example if we've explicitly
        // configured the connection pool to set it already).
        // 省略

        // Bind the session holder to the thread.
        // 綁定數(shù)據(jù)庫(kù)連接到當(dāng)前線程怎茫,這里的key是dataSource收壕,所以如果事務(wù)中換了dataSource那事務(wù)就不生效了
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
        }
    }

    catch (Throwable ex) {
        // 釋放連接回連接池
        // 當(dāng)前線程持有的連接句柄也一并釋放
    }
}

到此為止,事務(wù)的信息全部準(zhǔn)備好了轨蛤,事務(wù)也開啟了蜜宪,這個(gè)時(shí)候業(yè)務(wù)方法就是在事務(wù)中執(zhí)行了(如果配置了需要事務(wù)的話)

<-------------------------------我是罪惡的分割線------------------------------------>

事務(wù)執(zhí)行完畢是需要進(jìn)行資源的清理和釋放的,spring在開啟事務(wù)的時(shí)候綁定了很多信息到線程中祥山,而現(xiàn)在的應(yīng)用出于資源和性能的考慮圃验,基本用的都是連接池和線程池,會(huì)有復(fù)用的可能性缝呕,如果資源的釋放或者清理不到位损谦,會(huì)有莫名其妙的問(wèn)題出現(xiàn)(我這一次的問(wèn)題就是這么來(lái)的。岳颇。照捡。當(dāng)然不是框架的問(wèn)題,是自己操作有誤)话侧。

commitTransactionAfterReturning

這個(gè)方法是在業(yè)務(wù)方法正常返回后執(zhí)行的栗精,如果當(dāng)前是存在事務(wù)的,則會(huì)調(diào)用事務(wù)管理器的commit方法

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());
    }
}
  1. commit方法

這里會(huì)有一些標(biāo)志位的檢測(cè)瞻鹏,如果設(shè)置為true悲立,那事務(wù)是不會(huì)提交的,會(huì)回滾新博。比如單元測(cè)試的時(shí)候不管什么情況我們都不想提交薪夕,spring就是靠這個(gè)標(biāo)志位實(shí)現(xiàn)的。processRollback方法會(huì)在后面分析回滾的時(shí)候用到赫悄,這里先略過(guò)原献。

public final void commit(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
                "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    if (defStatus.isLocalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Transactional code has requested rollback");
        }
        processRollback(defStatus);
        return;
    }
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
        }
        processRollback(defStatus);
        // Throw UnexpectedRollbackException only at outermost transaction boundary
        // or if explicitly asked to.
        if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
            throw new UnexpectedRollbackException(
                    "Transaction rolled back because it has been marked as rollback-only");
        }
        return;
    }

    processCommit(defStatus);
}

題外話

還記得TransactionSynchronizationManager這個(gè)類嗎馏慨?里面維護(hù)了當(dāng)前線程的一些信息,其中有一個(gè)就是TransactionSynchronization的列表姑隅,我們可以自定義實(shí)現(xiàn)一個(gè)TransactionSynchronization然后在事務(wù)中綁定到當(dāng)前線程写隶,這樣可以實(shí)現(xiàn)在事務(wù)提交前或者提交后或者完成后執(zhí)行一些我們自定義的操作。這次出現(xiàn)的問(wèn)題就是因?yàn)槲覀儤I(yè)務(wù)代碼里有自定義實(shí)現(xiàn)的TransactionSynchronization讲仰,至于具體原因后面再詳述慕趴。

  1. processCommit方法

各個(gè)方法做的事見(jiàn)中文注釋,這里要注意cleanupAfterCompletion方法鄙陡,會(huì)去清理相關(guān)的資源冕房。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;
        try {
            prepareForCommit(status);
            // 調(diào)用當(dāng)前線程的TransactionSynchronization列表的對(duì)應(yīng)方法
            // 這里注意,beforeCompletion方法的異常是會(huì)被吞掉的趁矾,beforeCommit的異常則會(huì)傳播出去
            triggerBeforeCommit(status);
            triggerBeforeCompletion(status);
            beforeCompletionInvoked = true;
            boolean globalRollbackOnly = false;
            if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                globalRollbackOnly = status.isGlobalRollbackOnly();
            }
            //如果用到了spring的NESTED傳播耙册,底層用到了數(shù)據(jù)庫(kù)的savePoint,所以這里會(huì)釋放
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Releasing transaction savepoint");
                }
                status.releaseHeldSavepoint();
            }
            // 只有最外層的事務(wù)這里才是true
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction commit");
                }
                // 這里調(diào)用底層數(shù)據(jù)庫(kù)連接的commit方法提交事務(wù)
                doCommit(status);
            }
            // Throw UnexpectedRollbackException if we have a global rollback-only
            // marker but still didn't get a corresponding exception from commit.
            if (globalRollbackOnly) {
                throw new UnexpectedRollbackException(
                        "Transaction silently rolled back because it has been marked as rollback-only");
            }
        }
        catch (UnexpectedRollbackException ex) {
            // can only be caused by doCommit
            // 這里調(diào)用TransactionSynchronization列表的afterCompletion方法愈魏,會(huì)吞掉異常
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
            throw ex;
        }
        catch (TransactionException ex) {
            // can only be caused by doCommit
            if (isRollbackOnCommitFailure()) {
                // 如果提交異常這里會(huì)回滾事務(wù)觅玻,里層也是調(diào)用TransactionSynchronization列表的afterCompletion方法
                // 只不過(guò)如果回滾失敗想际,事務(wù)狀態(tài)就是未知
                doRollbackOnCommitException(status, ex);
            }
            else {
                // 單純調(diào)用TransactionSynchronization列表的afterCompletion方法
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            }
            throw ex;
        }
        catch (RuntimeException ex) {
            if (!beforeCompletionInvoked) {
                // 如果前面beforeCompletion未調(diào)用培漏,則這里調(diào)一次
                triggerBeforeCompletion(status);
            }
            // 回滾事務(wù)
            doRollbackOnCommitException(status, ex);
            throw ex;
        }
        catch (Error err) {
            if (!beforeCompletionInvoked) {
                triggerBeforeCompletion(status);
            }
            doRollbackOnCommitException(status, err);
            throw err;
        }

        // Trigger afterCommit callbacks, with an exception thrown there
        // propagated to callers but the transaction still considered as committed.
        try {
            // 調(diào)用TransactionSynchronization列表的afterCommit方法
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }

    }
    finally {
        cleanupAfterCompletion(status);
    }
}
  1. cleanupAfterCompletion方法

這里關(guān)于資源的清理和釋放操作比較多,稍有不慎胡本,萬(wàn)劫不復(fù)啊牌柄。。侧甫。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    // 將事務(wù)狀態(tài)設(shè)為已完成
    status.setCompleted();
    // 最外層事務(wù)會(huì)去清理線程綁定的資源珊佣,包含TransactionSynchronization列表
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    if (status.isNewTransaction()) {
        // 從當(dāng)前線程綁定的資源中移除數(shù)據(jù)庫(kù)連接句柄
        // 將連接的一些屬性重置,恢復(fù)默認(rèn)值
        // 將連接還給連接池(如果沒(méi)用連接池會(huì)直接關(guān)閉連接)
        // 解除事務(wù)與連接的綁定關(guān)系
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 用于事務(wù)的掛起和恢復(fù)披粟,這里先略過(guò)
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction after completion of inner transaction");
        }
        resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

completeTransactionAfterThrowing

業(yè)務(wù)方法拋出異常后會(huì)執(zhí)行本方法咒锻,主要就是事務(wù)的回滾以及定義的TransactionSynchronization列表的關(guān)聯(lián)事務(wù)方法的執(zhí)行,上面有提到守屉,這里就不詳述了惑艇。

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                    "] after exception: " + ex);
        }
        if (txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 里層調(diào)用的就是processRollback方法
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                logger.error("Application exception overridden by rollback exception", ex);
                ex2.initApplicationException(ex);
                throw ex2;
            }
            catch (RuntimeException ex2) {
                logger.error("Application exception overridden by rollback exception", ex);
                throw ex2;
            }
            catch (Error err) {
                logger.error("Application exception overridden by rollback error", ex);
                throw err;
            }
        }
        else {
            // We don't roll back on this exception.
            // Will still roll back if TransactionStatus.isRollbackOnly() is true.
            try {
                // 如果拋出的異常不屬于回滾異常范圍內(nèi),則事務(wù)依然提交
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {
                logger.error("Application exception overridden by commit exception", ex);
                ex2.initApplicationException(ex);
                throw ex2;
            }
            catch (RuntimeException ex2) {
                logger.error("Application exception overridden by commit exception", ex);
                throw ex2;
            }
            catch (Error err) {
                logger.error("Application exception overridden by commit error", ex);
                throw err;
            }
        }
    }
}
  1. processRollback方法

這里很多方法前面都有提到拇泛,不詳述了滨巴。

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            triggerBeforeCompletion(status);
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Rolling back transaction to savepoint");
                }
                status.rollbackToHeldSavepoint();
            }
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction rollback");
                }
                // 調(diào)用數(shù)據(jù)庫(kù)連接的回滾方法
                doRollback(status);
            }
            else if (status.hasTransaction()) {
                if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                    if (status.isDebug()) {
                        logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                    }
                    doSetRollbackOnly(status);
                }
                else {
                    if (status.isDebug()) {
                        logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                    }
                }
            }
            else {
                logger.debug("Should roll back transaction but cannot - no transaction available");
            }
        }
        catch (RuntimeException ex) {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw ex;
        }
        catch (Error err) {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw err;
        }
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    }
    finally {
        cleanupAfterCompletion(status);
    }
}

總結(jié)

到此為止,spring關(guān)于事務(wù)的處理的源碼差不多分析完了俺叭,回到正題恭取,為啥會(huì)出現(xiàn)連接已關(guān)閉的情況呢?因?yàn)槲覀冏远x了一個(gè)TransactionSynchronization來(lái)實(shí)現(xiàn)事務(wù)事件觸發(fā)機(jī)制熄守,并且在TransactionSynchronization的afterCompletion方法中操作了Dao層蜈垮,也就是用到了數(shù)據(jù)庫(kù)連接耗跛。看一下afterCompletion方法的注釋窃款,里面有提到這個(gè)時(shí)候事務(wù)已經(jīng)提交或者回滾了课兄,但是相關(guān)資源可能還沒(méi)有釋放,所以一旦有與數(shù)據(jù)庫(kù)連接相關(guān)的代碼晨继,可能會(huì)參與到前面的事務(wù)中去烟阐。如果這里非要執(zhí)行與數(shù)據(jù)庫(kù)連接相關(guān)的操作,spring建議明確標(biāo)注紊扬,并且使用新開事務(wù)的傳播機(jī)制蜒茄。框架封裝好餐屎,使用需謹(jǐn)慎啊檀葛。

/**
 * Invoked after transaction commit/rollback.
 * Can perform resource cleanup <i>after</i> transaction completion.
 * <p><b>NOTE:</b> The transaction will have been committed or rolled back already,
 * but the transactional resources might still be active and accessible. As a
 * consequence, any data access code triggered at this point will still "participate"
 * in the original transaction, allowing to perform some cleanup (with no commit
 * following anymore!), unless it explicitly declares that it needs to run in a
 * separate transaction. Hence: <b>Use {@code PROPAGATION_REQUIRES_NEW}
 * for any transactional operation that is called from here.</b>
 * @param status completion status according to the {@code STATUS_*} constants
 * @throws RuntimeException in case of errors; will be <b>logged but not propagated</b>
 * (note: do not throw TransactionException subclasses here!)
 * @see #STATUS_COMMITTED
 * @see #STATUS_ROLLED_BACK
 * @see #STATUS_UNKNOWN
 * @see #beforeCompletion
 */
void afterCompletion(int status);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腹缩,隨后出現(xiàn)的幾起案子屿聋,更是在濱河造成了極大的恐慌,老刑警劉巖藏鹊,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件润讥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盘寡,警方通過(guò)查閱死者的電腦和手機(jī)楚殿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)竿痰,“玉大人脆粥,你說(shuō)我怎么就攤上這事∮吧妫” “怎么了变隔?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蟹倾。 經(jīng)常有香客問(wèn)我匣缘,道長(zhǎng),這世上最難降的妖魔是什么喊式? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任孵户,我火速辦了婚禮,結(jié)果婚禮上岔留,老公的妹妹穿的比我還像新娘夏哭。我一直安慰自己,他們只是感情好献联,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布竖配。 她就那樣靜靜地躺著何址,像睡著了一般。 火紅的嫁衣襯著肌膚如雪进胯。 梳的紋絲不亂的頭發(fā)上用爪,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音胁镐,去河邊找鬼偎血。 笑死,一個(gè)胖子當(dāng)著我的面吹牛盯漂,可吹牛的內(nèi)容都是我干的颇玷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼就缆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帖渠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起竭宰,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤空郊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后切揭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狞甚,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年伴箩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了入愧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鄙漏。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗤谚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怔蚌,到底是詐尸還是另有隱情巩步,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布桦踊,位于F島的核電站椅野,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏籍胯。R本人自食惡果不足惜竟闪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杖狼。 院中可真熱鬧炼蛤,春花似錦、人聲如沸蝶涩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嗽上,卻和暖如春次舌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兽愤。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工彼念, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浅萧。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓国拇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惯殊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酱吝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容