2011-03-18 42 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()