2010-08-16 115 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