事件发布与监听
@EventListener
在Spring框架下,默认被@EventListener的方法是同步的,即事件发布线程会阻塞等待所有监听器方法执行完毕后再继续执行;
而且如果发布事件的方法处在事务中,那么监听器也默认和事件发布处于同一事务中;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class MyEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publishEvent() {
MyEvent event = new MyEvent(this);
// 发布事件
applicationEventPublisher.publishEvent(event);
}
}
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventListener {
@EventListener
public void handleMyEvent(MyEvent event) {
// 这个方法是同步执行的,会阻塞事件发布线程
System.out.println("Handling event: " + event);
}
}
当然,我们可以通过@Async注解使事件监听器方法异步执行,而且也顺便解决了在同一事务的问题,即事件发布和事件监听处在不同事务中
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class MyAsyncEventListener {
@Async
@EventListener
public void handleMyEvent(MyEvent event) {
// 这个方法是异步执行的,不会阻塞事件发布线程
System.out.println("Handling event asynchronously: " + event);
}
}
@TransactionalEventListener
现在有一个新的需求:
1、创建一个用户
2、用户创建成功后,发送邮件给用户
可能你会很轻易的想到如下代码逻辑:
@Transactional
public User createUser(User user) {
User newUser = userRepository.save(user);
emailService.sendEmail(user.getEmail());
return newUser;
}
但仔细想想,上述代码存在两个问题:
1、如果邮件发送失败,事务发生回滚,从而导致用户创建失败
2、邮件发送成功后,事务提交失败,导致用户收到了邮件,但账号创建失败
可能你又会想到加入事件监听器来完成解耦
/**
* 事件发布
*/
@Transactional
public Customer createCustomer(User user) {
User newUser = userRepository.save(user);
final UserCreatedEvent event = new UserCreatedEvent(newUser);
applicationEventPublisher.publishEvent(event);
return newUser;
}
/**
* 事件监听
*/
@Component
public class UserCreatedEventListener {
private final EmailService emailService;
public UserCreatedEventListener(EmailService emailService) {
this.emailService = emailService;
}
@EventListener
public void processUserCreatedEvent(UserCreatedEvent event) {
emailService.sendEmail(event.getUser().getEmail());
}
}
但是@EventListener 是和事件发布处于同一事务中, 其结果是和之前的代码一样;
如果加上@Async注解,也可能会出现邮件发送成功但用户创建失败的情况
如果不加事务会怎么样呢?如果事件能发布,则说明用户创建成功,那么对于邮件是否发送成功,其实对于业务来说以及不重要了,好像问题真的已经被解决!有那么一丢丢尴尬
但我还是想说另外一种解决方案(适用于有事务的情况):@TransactionalEventListener
参数phase介绍:
BEFORE_COMMIT:在事务提交前执行
AFTER_COMMIT:在事务提交后执行
AFTER_ROLLBACK:在事务回滚后执行
AFTER_COMPLETION:在事务完成时执行,这里的完成是指无论事务是成功提交还是事务回滚了
参数fallbackExecution介绍:
如果该事件不在事务中,且值为flase,事件将不会被处理,直接被丢弃
如果该事件不在事务中,且值为true,则直接执行目标方法,效果和@EventListener一样
@EventListener在底层上还是同一个事务,且是同步的,即事件监听器会阻塞事件发布线程,直到监听器方法执行完毕;当然也可以使用@Async注解使标记方法异步执行
也就是说该注解可以实现:事件发布线程事务提交之后再去执行目标方法,也就真正做到用户创建和邮件发布解耦
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
2023-02-27 对list.stream().map()的理解
2023-02-27 FileInputStream中的读入方式
2023-02-27 SpringBoot 拦截器、过滤器、监听器