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底層也是這樣實現的。