前言
我們都知道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。
入口
- 首先是事務(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();
}
});
}
- 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);
}
- 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);
}
}
- 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());
}
}
- 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讲仰,至于具體原因后面再詳述慕趴。
- 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);
}
}
- 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;
}
}
}
}
- 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);