2011-03-18 235 views
10

我有一個springframework應用程序,我想將事務偵聽器添加到當前正在進行的事務中。動機是觸發通知下游系統的提交後行動。我使用@Transactional來圍繞一些服務方法來包裝事務 - 這是我想創建/註冊事務處理偵聽器的地方。我想做一些「喜歡」以下的事情。用spring動態註冊事務監聽器?

public class MyService { 
@Transaction 
public void doIt() { 
    modifyObjects(); 

    // something like this 
    getTransactionManager().registerPostCommitAction(new 
    TransactionSynchronizationAdapter() { 
    public void afterCommit() { 
     notifyDownstream(); 
    } 
    }); 
} 
} 

Spring有一個TransactionSynchronization接口和適配器類,它看起來正是我想要的;但是如何動態地註冊一個當前事務或事務管理器並不是很清楚。如果我可以避免它,我寧願不繼承JtaTransactionManager的子類。

問:有沒有人曾經這樣做過。

問:什麼是最簡單的方法來註冊我的適配器?

回答

3

,你可以在你的服務使用aspect to match事務方法方面來實現:

@Aspect 
public class AfterReturningExample { 

    @AfterReturning("execution(* com.mypackage.MyService.*(..))") 
    public void afterReturning() { 
    // ... 
    } 

} 
+0

但是,一個好的建議不會在任何交易中執行?我只想要完成任務。另外,我可以使用默認彈簧(我不使用代碼編織)的AOP註釋只是JDK代理。 – Justin 2011-03-18 21:14:33

+0

當方法拋出異常時,返回通知不會運行,所以這依賴於@Transactional在方法返回結果時提交併在拋出異常時回滾,這通常會發生。這些方面是通過Spring動態代理來調用的,因此它不會執行任何代碼編織(除非您不提供用於Spring的代理接口,其中spring將退回到在運行時使用cglib編譯代碼)。 – krock 2011-03-18 21:20:14

+1

+1有趣,但我仍然不確信沒有測試。至少有2個@AfterReturning參與:@Transactional,然後我的自定義方面。如果@Transactional運行,那麼我的運行一切都很好;但是,我將在之前執行的另一個命令中 - 與事務提交狀態無關。 – Justin 2011-03-18 23:52:24

26

其實這並不難,因爲我想, Spring有一個靜態的輔助類,它把'正確'的東西放到線程上下文中。

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() { 
     @Override 
     public void afterCommit() { 
      s_logger.info("TRANSACTION COMPLETE!!!"); 
     } 
    } 
); 
+0

+1爲一個很好和直接的答案。我不知道TransactionSynchronizationManager存在。 – krock 2011-03-19 00:21:58

+0

實際上這個解決方案並不像我那樣好。 TransactionSynchronizationManager的合同更加廣泛,並且試圖從afterCommit()發送JMS消息失敗(無一例外),因爲上面的代碼確實遵循了合同。 – Justin 2011-03-22 18:26:43

+0

大多數Spring消息傳遞解決方案(JMS,AMQP)允許您在事務管理器中插入它們,以便兩個系統(db,消息代理)之間的事務同步。我不想這樣做,因爲RabbitMQ TX非常慢。因此你可能想看看我的解決方案。 – 2013-02-27 15:54:41

2

這裏是一個更完整的解決方案我做了一個類似的問題與希望的事務被提交後發送我的郵件(我可以使用RabbitMQ的TX但他們寧可慢)。

public class MessageBusUtils { 
    public static Optional<MessageBusResourceHolder> getTransactionalResourceHolder(TxMessageBus messageBus) { 

     if (! TransactionSynchronizationManager.isActualTransactionActive()) { 
      return Optional.absent(); 
     } 

     MessageBusResourceHolder o = (MessageBusResourceHolder) TransactionSynchronizationManager.getResource(messageBus); 
     if (o != null) return Optional.of(o); 

     o = new MessageBusResourceHolder(); 
     TransactionSynchronizationManager.bindResource(messageBus, o); 
     o.setSynchronizedWithTransaction(true); 
     if (TransactionSynchronizationManager.isSynchronizationActive()) { 
      TransactionSynchronizationManager.registerSynchronization(new MessageBusResourceSynchronization(o, messageBus)); 
     } 
     return Optional.of(o); 

    } 

    private static class MessageBusResourceSynchronization extends ResourceHolderSynchronization<MessageBusResourceHolder, TxMessageBus> { 
     private final TxMessageBus messageBus; 
     private final MessageBusResourceHolder holder; 

     public MessageBusResourceSynchronization(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey) { 
      super(resourceHolder, resourceKey); 
      this.messageBus = resourceKey; 
      this.holder = resourceHolder; 
     } 


     @Override 
     protected void cleanupResource(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey, 
       boolean committed) { 
      resourceHolder.getPendingMessages().clear(); 
     } 

     @Override 
     public void afterCompletion(int status) { 
      if (status == TransactionSynchronization.STATUS_COMMITTED) { 
       for (Object o : holder.getPendingMessages()) { 
        messageBus.post(o, false); 
       } 
      } 
      else { 
       holder.getPendingMessages().clear(); 
      } 
      super.afterCompletion(status); 
     } 


    } 
} 

public class MessageBusResourceHolder extends ResourceHolderSupport { 

    private List<Object> pendingMessages = Lists.newArrayList(); 

    public void addMessage(Object message) { 
     pendingMessages.add(message); 
    } 


    protected List<Object> getPendingMessages() { 
     return pendingMessages; 
    } 

} 

現在,在你的類,你實際發送你會做

@Override 
public void postAfterCommit(Object o) { 
    Optional<MessageBusResourceHolder> holder = MessageBusTxUtils.getTransactionalResourceHolder(this); 
    if (holder.isPresent()) { 
     holder.get().addMessage(o); 
    } 
    else { 
     post(o, false); 
    } 
} 

對不起,長篇大論的編碼樣本,但希望這將顯示一個人怎麼辦後提交的東西消息。

+0

我使用您的代碼在提交事務後更新3 solr索引。我還使用Spring事件來檢測Business Objects的修改,將事件存儲在ResourceHolder中,並更新需要異步更新的索引。 – 2013-10-09 15:10:37

0

在提交和回滾方法上重寫事務管理器是否有意義,在開始時調用super.commit()