gpt4 book ai didi

java - 使用 Spring @TransactionalEventListener 发布事件时的奇怪(循环)行为

转载 作者:IT老高 更新时间:2023-10-28 13:50:28 27 4
gpt4 key购买 nike

我有一个奇怪的问题,涉及 @TransactionalEventListener 无法正确触发或在由另一个 @TransactionalEventListener 触发时出现预期行为。

一般流程是:

  • AccountService 发布事件(到 AccountEventListener)
  • AccountEventListener 监听事件
  • 执行一些处理,然后发布另一个事件(到 MailEventListener)
  • MailEventListener 监听事件并执行一些处理

所以这里是类(摘录)。

public class AccountService {

@Transactional
public User createAccount(Form registrationForm) {

// Some processing

// Persist the entity
this.accountRepository.save(userAccount);

// Publish the Event
this.applicationEventPublisher.publishEvent(new RegistrationEvent());
}
}

public class AccountEventListener {

@TransactionalEventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public MailEvent onAccountCreated(RegistrationEvent registrationEvent) {

// Some processing

// Persist the entity
this.accountRepository.save(userAccount);

return new MailEvent();
}
}

public class MailEventListener {

private final MailService mailService;

@Async
@EventListener
public void onAccountCreated(MailEvent mailEvent) {

this.mailService.prepareAndSend(mailEvent);
}
}

此代码有效,但我的意图是在我的 MailEventListener 类中使用 @TransactionalEventListener。因此,当我在 MailEventListener 类中从 @EventListener 更改为 @TransactionalEventListener 的那一刻。 MailEvent 不会被触发。

public class MailEventListener {

private final MailService mailService;

@Async
@TransactionalEventListener
public void onAccountCreated(MailEvent mailEvent) {

this.mailService.prepareAndSend(mailEvent);
}
}

MailEventListener 从未被触发。于是我去查看Spring Documentation ,并且它声明 @Async @EventListener 不支持由另一个事件的返回发布的事件。所以我改为在我的 AccountEventListener 类中使用 ApplicationEventPublisher

public class AccountEventListener {

@TransactionalEventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void onAccountCreated(RegistrationEvent registrationEvent) {

// Some processing

this.accountRepository.save(userAccount);

this.applicationEventPublisher.publishEvent(new MailEvent());
}
}

一旦我更改为上述内容,我的 MailEventListener 现在将接收从 AccountEventListener 发送的事件,但在提交表单时网页会挂起并引发一些异常过了一会儿,它还向我的电子邮件帐户发送了大约 9 封相同的电子邮件。

我添加了一些日志记录,发现我的 AccountEventListener (this.accountRepository.save()) 在遇到异常之前实际上运行了 9 次,然后导致我的MailEventListener 我相信执行 9 次,这就是为什么我在收件箱中收到了 9 封邮件。

这是 Pastebin 中的日志.

我不确定为什么以及是什么导致它运行 9 次。我的方法中没有循环或任何内容,无论是在 AccountServiceAccountEventListener 还是 MailEventListener 中。

谢谢!

最佳答案

So I went to view Spring Documentation, and it states that @Async @EventListener is not support for event that is published by the return of another event. And so I changed to using ApplicationEventPublisher in my AccountEventListener class.

你的理解不正确。

The document said that :

This feature is not supported for asynchronous listeners.

不代表

它声明@Async @EventListener 不支持通过返回另一个事件发布的事件。

意思是:

此功能不支持从@Async @EventListener 返回的事件。

您的设置:

@Async
@TransactionalEventListener
public void onAccountCreated(MailEvent mailEvent) {

this.mailService.prepareAndSend(mailEvent);
}

不起作用because as stated in document :

If the event is not published within the boundaries of a managed transaction, the event is discarded unless the fallbackExecution() flag is explicitly set. If a transaction is running, the event is processed according to its TransactionPhase.

如果你使用调试,你可以看到如果你的事件是从事件监听器返回的,它发生在事务提交之后,因此该事件被丢弃了。

因此,如果您按照文档中所述设置 fallbackExecution = true,您的事件将正确监听:

@Async
@TransactionalEventListener(fallbackExecution = true)
public void onAccountCreated(MailEvent mailEvent) {

this.mailService.prepareAndSend(mailEvent);
}

重复的行为看起来像一些重试行为,连接排队,耗尽池并抛出异常。除非您提供最小的源代码来重现问题,否则我无法识别它。

更新

阅读您的代码,根本原因现在很清楚了。

查看 POST/registerPublisherCommon

的设置
  1. MailPublisherCommonEventAccountPublisherCommonEventBaseEvent
  2. 的子事件
  3. createUserAccountPublisherCommon 发布 AccountPublisherCommonEvent
  4. 类型的事件
  5. MailPublisherCommonEventListener 已注册以处理 MailPublisherCommonEvent
  6. AccountPublisherCommonEventListener 已注册以处理 BaseEvent它的所有子事件
  7. AccountPublisherCommonEventListener 还发布 MailPublisherCommonEvent(这也是一个 BaseEvent)。

阅读4 + 5你会看到根本原因:AccountPublisherCommonEventListener发布MailPublisherCommonEvent也是自己处理的,因此发生无限事件处理。

要解决它,只需像您一样缩小它可以处理的事件类型。

注意

您的 MailPublisherCommonEvent 设置不管 fallbackExecution 标志都可以工作,因为您发布的是 INSIDE A TRANSACTION,而不是 OUTSIDE A TRANSACTION (通过从事件监听器返回)就像您在问题中指定的那样。

关于java - 使用 Spring @TransactionalEventListener 发布事件时的奇怪(循环)行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51769718/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com