【问题标题】:How can I use Spring Integration to only send a message if my transaction finishes successfully?如果我的事务成功完成,如何使用 Spring Integration 仅发送消息?
【发布时间】:2014-02-12 22:13:44
【问题描述】:

我正在学习 Spring Integration 并使用它在 Grails 中实现一个基本的电子邮件服务。我想要做的是调用我的电子邮件服务,但只有在尝试发送电子邮件的交易成功时才会发送电子邮件。虽然这是在 Grails 中完成的,但它实际上应该与常规 Spring 应用程序没有什么不同,除了使用 BeanBuilder DSL 而不是 XML 配置。

无论如何,这是我对频道的配置:

beans = {
    xmlns integration:'http://www.springframework.org/schema/integration'
    integration.channel(id: 'email')
}

这是我的服务:

class MailService {

    @ServiceActivator(inputChannel = "email")
    MailMessage sendMail(Closure callable) {
        //sending mail code
    }
}

现在我期望发生的是,当我将此 MailService 注入另一个服务并调用发送邮件时,这将在电子邮件通道上放置一条消息,只有在我的事务完成时才会发布。让我相信这是关于 UserProcess 的部分:http://docs.spring.io/spring-integration/reference/html/transactions.html,它指出用户启动的进程将具有 Spring 提供的所有事务属性。

我正在尝试通过集成测试对此进行测试:

void "test transactionality"() {
        when:
        assert DomainObject.all.size() == 0
        DomainObject.withNewTransaction { status ->
            DomainObject object = buildAndSaveNewObject()
            objectNotificationService.sendEmails(object) //This service injects emailService and calls sendMail

            throw new Exception()
        }

        then:
        thrown(Exception) // This is true
        DomainObject.all.size() == 0 // This is true
        greenMail.receivedMessages.length == 0 // This fails
    }

这样做是在同一个事务中创建和保存一个对象并发送电子邮件。然后我抛出一个异常导致该事务失败。正如预期的那样,我的域对象都没有被持久化。但是,我仍然收到电子邮件。

总的来说,我对 Spring Integration 和 Spring 还是很陌生,所以我可能会误解这一切应该如何工作,但我希望 sendMail 消息永远不会放在电子邮件通道上。

【问题讨论】:

    标签: spring grails transactions spring-integration


    【解决方案1】:

    我不知道这将如何在 Grails 中完成,但在 Java 中,您可以使用 transaction synchronization factory 从而根据成功/失败采取不同的操作...

    <int:transaction-synchronization-factory id="syncFactory">
        <int:after-commit expression="payload.renameTo('/success/' + payload.name)" channel="committedChannel" />
        <int:after-rollback expression="payload.renameTo('/failed/' + payload.name)" channel="rolledBackChannel" />
    </int:transaction-synchronization-factory>
    

    表达式评估的结果被发送到通道,您可以在其中拥有出站邮件适配器。

    【讨论】:

    • 谢谢,这与我正在寻找的内容相同。不过,我相信这仅适用于轮询器,主要用于使用事务管理器将异步操作视为发生在单个事务中。它采用非事务性过程并对其进行模仿。就我而言,我有一个实际的同步事务,我只想在该事务提交时发送一条消息。
    • 这总是一个问题(将非事务性资源与事务性资源同步)。一旦提交,您就可以在事务之外发送邮件。也许另一种解决方案可能是编写一个虚拟事务管理器(它将发送电子邮件)并将两个事务管理器包装在 ChainedTransactionManager 中,在 Dave Syer 的优秀文章中的 Best Efforts 1PC 部分中讨论过:javaworld.com/article/2077963/open-source-tools/…
    • 我最终实现了一个使用 TransactionSynchronizationManager 的 TransactionService。我会尽快添加一个使用的答案。虽然它根本不使用 SI,所以我会接受你的回答,因为它是你能用 SI 得到的最接近的东西。
    【解决方案2】:

    事实证明,我不认为 Spring Integration 是实现“仅在提交时执行”功能的最佳方式(但如果你这样做了,Gary Russell 的答案就是要走的路。)你应该改用提供的 TransactionSynchronizationManager作为 Spring 事务管理框架的一部分。

    例如,我在 grails 中创建了一个 TransactionService:

    class TransactionService {
    
        def executeAfterCommit(Closure c) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                void afterCommit() {
                    c.call()
                }
            })
        }
    }
    

    然后你可以在任何你需要的地方注入它并像这样使用它:

    def transactionService
    
    transactionService.executeAfterCommit {
         sendConfirmationEmail()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-15
      • 2019-06-08
      • 1970-01-01
      • 1970-01-01
      • 2017-03-13
      • 2019-06-16
      • 1970-01-01
      相关资源
      最近更新 更多