1. 程式人生 > >TransactionalEventListener實現事務監控

TransactionalEventListener實現事務監控

問題背景

在專案中,往往需要執行資料庫操作後,傳送訊息或事件來非同步呼叫其他元件執行相應的操作,例如:
使用者註冊後傳送啟用碼;
配置修改後傳送更新事件等。
但是,資料庫的操作如果還未完成,此時非同步呼叫的方法查詢資料庫發現沒有資料,這就會出現問題。

為了解決上述問題,Spring為我們提供了兩種方式:
(1) @TransactionalEventListener註解
(2) 事務同步管理器TransactionSynchronizationManager

1. @TransactionalEventListener註解

在Spring4.2+,有一種叫做TransactionEventListener的方式,能夠控制在事務的時候Event事件的處理方式。
我們知道,Spring的釋出訂閱模型實際上並不是非同步的,而是同步的來將程式碼進行解耦。而TransactionEventListener仍是通過這種方式,只不過加入了回撥的方式來解決,這樣就能夠在事務進行Commited,Rollback…等的時候才會去進行Event的處理。
程式碼如下:

@Service("fooService") 
public class FooServiceImpl implements FooService {
    private static final Logger LOGGER = Logger.getLogger(FooServiceImpl.class); 
    @Override 
    public void insertFoo(Foo foo) throws MyTransactionException { 
    	LOGGER.info("[fooService] start insert foo"); 
    	ApplicationEventPublisher eventPublisher =
EventPublisher.getApplicationEventPublisher(); if (null != eventPublisher) { eventPublisher.publishEvent(new MyTransactionEvent("test", this)); } LOGGER.info("[fooServive] finish insert foo"); } } public class MyTransactionEvent extends ApplicationEvent { private
String name; public MyTransactionEvent(String name, Object source) { super(source); this.name = name; } public String getName() { return this.name; } } @Component public class MyTransactionListener { private static final Logger LOGGER = Logger.getLogger(MyTransactionListener.class); @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void hanldeOrderCreatedEvent(MyTransactionEvent event) { LOGGER.info("transactionEventListener start"); // do transaction event LOGGER.info("event : " + event.getName()); // finish transaction event LOGGER.info("transactionEventListener finish"); } }

這樣,只有當前事務提交之後,才會執行事件監聽器的方法。其中引數phase預設為AFTER_COMMIT,共有四個列舉:

/** 
* Fire the event before transaction commit. 
* @see TransactionSynchronization#beforeCommit(boolean) */ 
BEFORE_COMMIT, 
/** 
* Fire the event after the commit has completed successfully. 
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and 
* therefore executes in the same after-completion sequence of events, 
* (and not in {@link TransactionSynchronization#afterCommit()}). 
* @see TransactionSynchronization#afterCompletion(int) 
* @see TransactionSynchronization#STATUS_COMMITTED */ AFTER_COMMIT, 
/** 
* Fire the event if the transaction has rolled back. 
* <p>Note: This is a specialization of {@link #AFTER_COMPLETION} and 
* therefore executes in the same after-completion sequence of events. 
* @see TransactionSynchronization#afterCompletion(int) 
* @see TransactionSynchronization#STATUS_ROLLED_BACK 
*/ 
AFTER_ROLLBACK, 
/** 
* Fire the event after the transaction has completed. 
* <p>For more fine-grained events, use {@link #AFTER_COMMIT} or 
* {@link #AFTER_ROLLBACK} to intercept transaction commit 
* or rollback, respectively. 
* @see TransactionSynchronization#afterCompletion(int) */ 
AFTER_COMPLETION

內部實現就是包裝@TransactionalEventListener註解的方法,添加了一個介面卡, ApplicationListenerMethodTransactionalAdapter,內部通過TransactionSynchronizationManager.registerSynchronization 註冊一個同步器釋出事務時,,記下event,然後註冊一個同步器TransactionSynchronizationEventAdapter,當事務提交後, TransactionSynchronizationManager會回撥上面註冊的同步介面卡,這裡註冊就是放入到一個ThreadLocal裡面,通過它來傳遞引數。這時,TransactionSynchronizationEventAdapter內部才會真正的去呼叫hanldeOrderCreatedEvent方法。

2. TransactionSynchronizationManager方法

這種方法是通過手動的來註冊回撥來實現的。

@EventListener 
public void afterRegisterSendMail(MessageEvent event) { 
    // Spring 4.2 之前 
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronizationAdapter() { 
        @Override 
        public void afterCommit() { 
            internalSendMailNotification(event); 
        } 
    }); 
}

@TransactionalEventListener底層也是這樣實現的。