【问题标题】:Spring @TransactionalEventListener not working as expectedSpring @TransactionalEventListener 没有按预期工作
【发布时间】:2016-10-25 04:36:20
【问题描述】:

我正在尝试使用 @TransactionalEventListener 监听事件,但该事件甚至在整个事务结束之前就已被监听。即使方法抛出异常也会监听事件。

这是我正在尝试做的示例代码

@Component
public class SampleListener{

    @TransactionalEventListener
    public void handleSomeTransactionalEvent(SampleEvent event){
      //Some Logic
    }
}

@Service
@Transactional
public class SampleInsertService{

     @Autowired
     private ApplicationEventPublisher applicationEventPublisher;

     public void someTransactionalMethod(SomeDTO someDTO){
          //Delete all call

          //Again Insert all calls

          //Publish event after insert
          applicationEventPublisher.publishEvent(new SampleEvent(this, String.EventType, someDTO));

          /** Some other call to DB which throws exception **/
     }

}


public class SampleEvent implements ApplicationEvent{

     private String eventType;

     public SampleEvent(Object source, String eventType, SampleDTO sampleDTO){
           //some logic
     }

}

在这种情况下,只要我的事务方法中的插入完成,我的侦听器就会被调用。我相信这应该在所有数据库调用结束并且提交完成后调用。我是不是错过了什么。我正在使用 Spring 4.3.0。请指导我。

【问题讨论】:

    标签: spring spring-transactions


    【解决方案1】:

    我发现了这个问题。如果您不使用 Spring MVC,则不会发生此问题。此问题是由于在 rootapplicationcontext 和子 webapplicationcontext(对于 Spring MVC)中加载的侦听器类而发生的。

    我在 application-context.xml 和 spring-web.xml 中都进行了通用组件扫描。因此,监听器类被加载了两次。一次用于 application-context.xml,再次用于 spring-web.xml。在删除 spring-web.xml 的通用组件扫描并在 spring-web.xml 中仅提及控制器包和过滤器包时,问题得到了解决。

    事务事件监听器在事务提交后只被调用一次。

    【讨论】:

    • 我只有 1 个被加载的 XML 配置文件。但是,我看到的症状和你一样。判断我的组件是否被加载两次的最佳方法是什么?
    • @DavidWebb ,如果您认为侦听器类可能会像我解释的场景那样被加载两次,您应该能够在 Spring 在应用程序启动时生成的日志中进行验证。我相信您可能必须打开 TRACE 级别才能获取有关初始化 Spring bean 的信息。如果您在启动期间在日志中发现它两次,则可能是问题所在。希望这会有所帮助。
    【解决方案2】:

    TransactionalEventListener 可以监听不同的TransactionPhase。您可以在注释中指定不同的阶段。在您的情况下,AFTER_COMMITAFTER_COMPLETION 可能有用。

    【讨论】:

    • 由于@TransactionalEventListener 默认阶段是AFTER_COMMIT,我没有提到阶段。我相信 handleSomeTransactionalEvent() 应该在所有数据库调用都在 Service 方法中结束后调用。但是,情况并非如此,它在执行最后一个 DB 调用之前和提交到 DB 之前被调用。如果我遗漏了什么,请告诉我。
    【解决方案3】:

    问题是 TransactionalEventListener 实现中的一个错误。它在事件处理执行器的线程上检查 TransactionSynchronizationManager.isSynchronizationActive(),但事务是在另一个线程(tomcat 线程池、jetty 线程池等)上创建的。所以在 Spring 4.2 中它只能在单线程应用程序中正常工作

    【讨论】:

      猜你喜欢
      • 2021-09-03
      • 2014-02-22
      • 2021-11-01
      • 2017-10-27
      • 1970-01-01
      • 2012-03-06
      • 2021-10-19
      • 2020-03-18
      • 2012-06-14
      相关资源
      最近更新 更多