2010-08-16 196 views
11

我在我的服務層使用Spring應用程序事件來通知更廣泛的系統發生特定事件。我遇到的問題是這些事件是在事務性上下文中觸發的(我使用Spring的@Transactional註釋)。危險在於我觸發了一個事件,然後事務在提交時失敗(可能在使用Hibernate時發生)。在這種情況下,事件偵聽器將假設某些狀態在執行後會回滾。這裏的危險是,例如,我可能發送了一封電子郵件來確認用戶在網站上的註冊,而事實上,他們的用戶帳戶並未實際創建。春季交易註釋 - 執行成功

有沒有一種方法,保留註釋的使用,在某處標記事件在事務提交後被觸發?這在使用Java Swing進行GUI編程時似乎有點類似於使用SwingUtilities.invokeLater(Runnable runnable)。我想說,一旦當前事務成功提交,稍後再執行此代碼。

任何想法?

感謝,

安德魯

+0

這一切都是在春季通過註釋本機處理了。 請參閱https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2https://spring.io/blog/2015/02/11/better特別是有關新的@TransactionalEventListener註釋的部分。 – PaulNUK 2015-12-16 08:15:32

回答

2

與確認電子郵件來解決問題的正確方法可能是使用一個分佈式事務與參與他們JMS資源。

然而,作爲一個快速和骯髒的解決方案,您可以嘗試創建一個PlatformTransactionManager將執行後,在一些ThreadLocal存儲註冊Runnable小號全成犯周圍的包裝(和回滾之後,從ThreadLocal刪除)。實際上,AbstractPlatformTransactionManager正好具有這種與TransactionSynchronization相同的邏輯,但它被深深嵌入其實現細節中。

+0

現在我已經與快速和骯髒的解決方案。我已經構建了一個基於ThreadLocal的機制,將一個Runnable對象的隊列與一個TransactionStatus對象相關聯。在TransactionStatus的commit()或rollback()上,我執行Runnable對象或丟棄它們。它工作得非常好。 – DrewEaster 2010-08-18 14:23:55

+0

你能提供一個代碼片段嗎? – Oliv 2013-01-29 13:42:11

+0

我不會說分佈式事務是正確的_proper_解決方案。如果僅發送郵件失敗,分佈式事務將會使整個事務失敗。但是這裏期望的行爲是郵件依賴於數據庫事務,而數據庫事務不依賴於郵件。 – yair 2013-04-30 20:05:58

4

你可以使用TransactionSynchronizationManager而不需要破解PlatformTransactionManager。

注意:TransactionAware是一個標記接口,指示ApplicationListener想要在成功提交事務後接收事件。

public class TransactionAwareApplicationEventMulticaster extends SimpleApplicationEventMulticaster { 

    @Override 
    public void multicastEvent(ApplicationEvent event) { 
     for (ApplicationListener listener : getApplicationListeners(event)) { 
      if ((listener instanceof TransactionAware) && TransactionSynchronizationManager.isSynchronizationActive()) { 
       TransactionSynchronizationManager.registerSynchronization(
        new EventTransactionSynchronization(listener, event)); 
      } 
      else { 
       notifyEvent(listener, event); 
      } 
     } 
    } 

    void notifyEvent(final ApplicationListener listener, final ApplicationEvent event) { 
      Executor executor = getTaskExecutor(); 
      if (executor != null) { 
       executor.execute(new Runnable() { 
        public void run() { 
         listener.onApplicationEvent(event); 
        } 
       }); 
      } 
      else { 
       listener.onApplicationEvent(event); 
      } 
    } 

    class EventTransactionSynchronization extends TransactionSynchronizationAdapter { 
     private final ApplicationListener listener; 
     private final ApplicationEvent event; 

     EventTransactionSynchronization(ApplicationListener listener, ApplicationEvent event) { 
      this.listener = listener; 
      this.event = event; 
     } 

     @Override 
     public void afterCompletion(int status) { 
      if ((phase == TransactionPhase.AFTER_SUCCESS) 
        && (status == TransactionSynchronization.STATUS_COMMITTED)) { 
       notifyEvent(listener, event); 
      } 
     } 
    } 
} 
10

這個工作對我來說:

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() { 
     @Override 
     public void afterCommit() { 
      // things to do when commited 
     } 
     // other methods to override are available too 
    }); 
+0

不錯!關於IMO問題的最佳答案。這與SwingUtilities.invokeLater最相似。 – 2013-06-21 09:51:11