spring的TransactionSynchronizationAdapter事務(wù)源碼解析
序
本文主要研究一下spring的TransactionSynchronizationAdapter
示例代碼
public void insert(TechBook techBook){
bookMapper.insert(techBook);
// send after tx commit but is async
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
System.out.println("send email after transaction commit...");
}
}
);
System.out.println("service end");
}使用TransactionSynchronizationManager.registerSynchronization注冊(cè)了一個(gè)TransactionSynchronizationAdapter,在其afterCommit方法也就是事務(wù)提交成功之后執(zhí)行一些額外邏輯
TransactionSynchronizationAdapter
org/springframework/transaction/support/TransactionSynchronizationAdapter.java
public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void suspend() {
}
@Override
public void resume() {
}
@Override
public void flush() {
}
@Override
public void beforeCommit(boolean readOnly) {
}
@Override
public void beforeCompletion() {
}
@Override
public void afterCommit() {
}
@Override
public void afterCompletion(int status) {
}
}TransactionSynchronizationAdapter是個(gè)抽象類,聲明實(shí)現(xiàn)TransactionSynchronization及Ordered接口
TransactionSynchronization
org/springframework/transaction/support/TransactionSynchronization.java
/**
* Invoked after transaction commit. Can perform further operations right
* <i>after</i> the main transaction has <i>successfully</i> committed.
* <p>Can e.g. commit further operations that are supposed to follow on a successful
* commit of the main transaction, like confirmation messages or emails.
* <p><b>NOTE:</b> The transaction will have been committed 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>
* @throws RuntimeException in case of errors; will be <b>propagated to the caller</b>
* (note: do not throw TransactionException subclasses here!)
*/
default void afterCommit() {
}注意這里注釋說明了異常不會(huì)被捕獲,而且建議不在這里拋出TransactionException的子類;另外對(duì)于afterCommit有數(shù)據(jù)庫相關(guān)操作的建議使用PROPAGATION_REQUIRES_NEW這個(gè)事務(wù)傳播級(jí)別,不然afterCommit的操作可能不會(huì)生效
registerSynchronization
org/springframework/transaction/support/TransactionSynchronizationManager.java
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
/**
* Register a new transaction synchronization for the current thread.
* Typically called by resource management code.
* <p>Note that synchronizations can implement the
* {@link org.springframework.core.Ordered} interface.
* They will be executed in an order according to their order value (if any).
* @param synchronization the synchronization object to register
* @throws IllegalStateException if transaction synchronization is not active
* @see org.springframework.core.Ordered
*/
public static void registerSynchronization(TransactionSynchronization synchronization)
throws IllegalStateException {
Assert.notNull(synchronization, "TransactionSynchronization must not be null");
Set<TransactionSynchronization> synchs = synchronizations.get();
if (synchs == null) {
throw new IllegalStateException("Transaction synchronization is not active");
}
synchs.add(synchronization);
}TransactionSynchronizationManager的registerSynchronization方法會(huì)把TransactionSynchronization注冊(cè)到當(dāng)前線程的synchronizations
processCommit
org/springframework/transaction/support/AbstractPlatformTransactionManager.java
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
private void triggerAfterCommit(DefaultTransactionStatus status) {
if (status.isNewSynchronization()) {
if (status.isDebug()) {
logger.trace("Triggering afterCommit synchronization");
}
TransactionSynchronizationUtils.triggerAfterCommit();
}
}AbstractPlatformTransactionManager的processCommit方法,在提交成功之后觸發(fā)triggerAfterCommit,這里調(diào)用了TransactionSynchronizationUtils.triggerAfterCommit(),注意這里沒有try catch,說明triggerAfterCommit的異常最終會(huì)拋給調(diào)用方
triggerAfterCommit
org/springframework/transaction/support/TransactionSynchronizationUtils.java
public static void triggerAfterCommit() {
invokeAfterCommit(TransactionSynchronizationManager.getSynchronizations());
}
public static void invokeAfterCommit(@Nullable List<TransactionSynchronization> synchronizations) {
if (synchronizations != null) {
for (TransactionSynchronization synchronization : synchronizations) {
synchronization.afterCommit();
}
}
}這里遍歷synchronizations執(zhí)行afterCommit方法,如果其中有一個(gè)有異常拋出則中斷
小結(jié)
使用TransactionSynchronizationManager.registerSynchronization可以在當(dāng)前線程的事務(wù)注冊(cè)一個(gè)TransactionSynchronizationAdapter,可以在afterCommit方法也就是事務(wù)提交成功之后執(zhí)行一些額外邏輯;注意這里拋出的異常不影響事務(wù)提交,但是異常不會(huì)被catch需要由調(diào)用方處理,對(duì)于afterCommit有數(shù)據(jù)庫相關(guān)操作的建議使用PROPAGATION_REQUIRES_NEW這個(gè)事務(wù)傳播級(jí)別,不然afterCommit的db操作可能不會(huì)生效。
在事務(wù)提交之后做一些事情可能不需要TransactionSynchronizationManager.registerSynchronization這種方式也能實(shí)現(xiàn),也就是需要額外一層來調(diào)用事務(wù)操作,有異常會(huì)拋出,沒有異常則執(zhí)行事務(wù)提交之后的事情,前提就是事務(wù)回滾異常不能被吞掉,不然外層調(diào)用可能以為事務(wù)成功了還有一種方式就是使用TransactionalEventListener,這種方式比TransactionSynchronizationManager.registerSynchronization更為優(yōu)雅一些
doc
以上就是spring的TransactionSynchronizationAdapter事務(wù)源碼解析的詳細(xì)內(nèi)容,更多關(guān)于spring TransactionSynchronizationAdapter的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MyBatis-Plus中的LambdaQuery用法實(shí)例詳解
LambdaQueryWrapper是MyBatis-Plus的類型安全查詢工具,通過Lambda表達(dá)式簡化條件構(gòu)建,避免硬編碼字段名,提升代碼可讀性、維護(hù)性及重構(gòu)友好性,適用于單表查詢場景,本文給大家介紹MyBatis-Plus中的LambdaQuery用法,感興趣的朋友一起看看吧2025-07-07
java 分轉(zhuǎn)元與元轉(zhuǎn)分實(shí)現(xiàn)操作
這篇文章主要介紹了java 分轉(zhuǎn)元與元轉(zhuǎn)分實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02
Eclipse配置tomcat發(fā)布路徑的問題wtpwebapps解決辦法
這篇文章主要介紹了Eclipse配置tomcat發(fā)布路徑的問題wtpwebapps解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06
應(yīng)用Java泛型和反射導(dǎo)出CSV文件的方法
這篇文章主要介紹了應(yīng)用Java泛型和反射導(dǎo)出CSV文件的方法,通過一個(gè)自定義函數(shù)結(jié)合泛型與反射的應(yīng)用實(shí)現(xiàn)導(dǎo)出CSV文件的功能,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12

