事件发布与监听

@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注解使标记方法异步执行

也就是说该注解可以实现:事件发布线程事务提交之后再去执行目标方法,也就真正做到用户创建和邮件发布解耦

 

参考文章

【1】@TransactionalEventListener注解的使用场景

posted @   先娶国王后取经  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 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 拦截器、过滤器、监听器
点击右上角即可分享
微信分享提示