1.美圖
2.概述
創建事務參考:
3.前文回顧
// 2.處理聲明式事物
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 2.2 創建事物
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
System.out.println("==創建了名爲:["+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.
// 2.3 繼續調用方法攔截器鏈,這裏一般將會調用目標類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
retVal = invocation.proceedWithInvocation();
} catch (Throwable ex) {
// target invocation exception
// 2.4 如果目標類方法拋出異常,則在此處理,例如:事物回滾
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
// 2.5 清除上一步創建的事物信息
cleanupTransactionInfo(txInfo);
}
// 2.6 調用成功完成後執行,但不是在異常被處理後執行。如果我們不創建事務,就什麼也不做。
commitTransactionAfterReturning(txInfo);
return retVal;
}
關於攔截器鏈的調用、目標方法的調用等前面都有介紹。
接下來的處理會分爲兩種情況(假如當前一定存在事物):
- 有異常,事物即有可能會被回滾、也有可能會被提交;
- 無異常,事物會被提交,
接下來對兩種情況逐個分析。因爲catch方法的代碼比較靠前,先分析有異常的情況。細心的同學可能會發現,無論是否拋出異常都會調用completeTransactionAfterThrowing
方法去完成事物,只是該方法被重載了,傳遞的參數不同而已,當然其內部處理細節肯定是不同的。
4.回滾判斷
/**
* 處理異常以完成事物,基於配置該事物可能回滾也可能提交
*
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 1.回滾
/**
* txInfo.transactionAttribute.rollbackOn(ex)判斷回滾的條件:
*
* 1. 如果自定了RollbackRuleAttribute列表,如果當前異常匹配到了RollbackRuleAttribute其中的條目,則回滾
* 例如:可以通過rollbackFor指定觸發回滾的異常@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
*
* 2. 否則如果異常是RuntimeException或者Error的類型,則回滾
*
* 3. 其他的異常是不會回滾的,這裏要注意一下...
*/
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 執行回滾
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
// 2.如果未能滿足回滾條件,則有可能會提交事物,也有可能會回滾事物
// 注意:如果TransactionStatus.isRollbackOnly()爲true,則仍然會執行回滾
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
txInfo.transactionAttribute.rollbackOn(ex)
這段代碼是對當前異常是否滿足回滾條件的判斷。Spring
事物中默認只有RuntimeException
和Error
兩種類型的被捕獲之後,事物纔會回滾。因爲在DefaultTransactionAttribute
類中有:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
但是在實際應用當中,可能會有很多自定義異常,如果想要自定義異常發生時也回滾事物,怎麼辦呢?這時候就要用到rollbackFor屬性,例如:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyException.class)
,Spring會將我們自定義的異常註冊到RuleBasedTransactionAttribute
類的RollbackRuleAttribute
屬性集合中。總結如下
- 匹配到自定義異常,回滾。
- 如果異常是
RuntimeException
或者Error
的類型,回滾。 - 其他異常,不回滾。
在completeTransactionAfterThrowing
方法的else
分支中通過commit
方法對當前事物進行了提交操作,但是,commit
方法並不一定會真正的提交事物,也有可能會回滾事物,這一點留在事物提交分析中講解,接下來看事物是如何回滾的。
5.回滾
/**
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*
* 執行回滾
*/
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 1.事物回滾前調用事物同步接口
triggerBeforeCompletion(status);
// 2.如果有保存點,則回滾到保存點
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
// 3.如果當前事物是一個新的事物,則調用doRollback執行給定事物的回滾
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
// 4.如果當前事物並非獨立事物,則將當前事物的rollbackOnly屬性標記爲true,等到事物鏈完成之後,一起執行回滾
// 如果當前存在事物,但是事物的rollbackOnly屬性已經被標記爲true
// 或者globalRollbackOnParticipationFailure(返回是否僅在參與事務失敗後纔將現有事務全局標記爲回滾)爲true
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 則將ConnectionHolder中的rollbackOnly標記爲true
doSetRollbackOnly(status);
}
// 5.如果當前不存在事物,則不執行任何操作
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");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
// 6.事物回滾後調用事物同步接口
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 7.事物完成後清理資源
cleanupAfterCompletion(status);
}
}
說明:第2、4點涉及到了嵌套事物回滾,請參考Spring : Spring @Transactional-嵌套事物回滾。
- 事物完成前、完成後觸發器的調用,例如:我們在上一節的業務方法裏註冊了
TransactionSynchronizationAdapter
接口,那麼在這裏會分別執行我們實現了接口的方法,我們可以通過該接口來做一些額外的功能擴展。 - 如果有保存點,則調用
rollbackToHeldSavepoint
回滾到保存點(後面嵌套事物章節分析) - 如果當前事物是一個新的事物,則調用
doRollback
執行給定事物的回滾 - 如果當前事物並非獨立事物,則將當前事物的
rollbackOnly
屬性標記爲true,等到事物鏈完成之後,一起執行回滾,大概的流程就是這樣了,因爲我們分析的是單service
下的單個業務方法調用,所以這裏我們還是隻分析最簡單的doRollback正常回滾調用(後面嵌套事物章節分析)
不同的事物管理器對doRollback回滾有不同的實現,這裏我們只看DataSourceTransactionManager的回滾方法:
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
從ConnectionHolder中拿到連接並執行回滾。這裏會涉及到一些數據庫底層的東西了,感興趣的同學可以自己深入研究。
到這裏事物的回滾操作就完成了,但是不要忘了,這裏一定要釋放掉事物相關的資源。以免造成數據庫連接泄露、內存泄露等錯誤。
/**
* Clean up after completion, clearing synchronization if necessary,
* and invoking doCleanupAfterCompletion.
* @param status object representing the transaction
* @see #doCleanupAfterCompletion
*/
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 1.將當前事物狀態標記爲已完成
status.setCompleted();
// 2.清除synchronization
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
// 3.事務完成後清理資源。
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
// 4.從嵌套事物中恢復被掛起的資源
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
第1、2很簡單,第4又涉及到了事物嵌套,我們只有分析一下第3步了:
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
// 解綁ConnectionHolder
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
// 重置連接
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
等到事物資源清理完成之後,事物就徹底的結束了。