自学内容网 自学内容网

Spring Event详解

1.详细介绍

Spring Event是Spring框架内建的一种发布/订阅(Publish-Subscribe)模式的实现,它允许应用内部不同组件之间通过事件进行通信。当某个特定事件发生时,系统中对这类事件感兴趣的监听器可以接收到通知并执行相应操作。

2.使用场景

  1. 内部模块间的通信:在一个Spring应用程序内部,不同服务或组件之间可以通过发布和监听事件来进行松耦合交互,比如在用户注册成功后触发邮件通知、权限更新等操作。
  2. 生命周期管理:Spring容器可以在Bean的生命周期中发布事件,如初始化完成后、销毁前等阶段,其他组件可以监听这些事件以执行相应的逻辑。
  3. 异步处理:虽然Spring Event默认是同步的,但也可以配置为异步传播,用于触发异步任务,比如用户行为跟踪、数据同步、日志记录、资源清理等。
  4. 业务流程编排:在复杂的业务流程中,事件驱动的方式有助于实现各个步骤之间的解耦,每个步骤作为独立的服务只关注处理特定的业务事件。

3.注意事项

  • 同步与异步:默认情况下,事件会被同步发送给所有监听器,这意味着如果监听器耗时较长,则会阻塞后续的监听器和发布线程。若需要异步处理,可以配置ApplicationEventMulticaster为异步模式。
  • 事务边界:在有事务控制的地方发布或消费事件时,需要注意事务的传播行为和一致性问题。例如,一个事务中的事件可能被另一个事务内的监听器处理,这可能导致预期之外的行为。
  • 资源释放:确保在监听器处理完事件后释放任何占用的资源,避免内存泄漏等问题。

服务关闭问题
Spring 广播消息时,Spring会在 ApplicationContext 中查找所有的监听者,即需要 getBean 获取 bean 实例。然而 Spring 有个限制————ApplicationContext 关闭期间,不得GetBean 否则会报错。
堆栈中的信息 解释了原因。Do not request a bean from a BeanFactory in a destroy method implementation

在应用上下文关闭时,不得从上下文中Get Bean。恰好,这个问题出现在服务关闭期间…

由于系统流量较高,日订单几百万,即便在低峰期单机的并发度也是比较高的,所以服务在关闭期间有少量流量进来或未处理完。这个场景下,使用 Spring Event 发布事件,Spring 无法正常广播事件,一定会出现异常,导致处理失败!

大家一定要切记!使用 SpringEvent 之前,一定要先治理服务,确保服务关闭时,先切断入口流量(Http、MQ、RPC),然后再关闭服务,关闭 Spring 上下文!

详细的分析请参考:https://juejin.cn/post/7281159113882468371

4.案例分析

假设有一个电商应用,在用户下单成功后,希望执行以下操作:

  • 发送订单确认邮件
  • 更新用户的积分信息
  • 向库存系统发送减库存请求

使用Spring Event的实现方式:

  1. 定义一个自定义事件类 OrderPlacedEvent,包含订单相关数据。
public class OrderPlacedEvent extends ApplicationEvent {
    private final Order order;

    public OrderPlacedEvent(Order source) {
        super(source);
        this.order = source;
    }

    public Order getOrder() {
        return order;
    }
}
  1. 创建事件监听器,继承ApplicationListener接口,并实现事件处理方法。
@Component
public class OrderEventListener implements ApplicationListener<OrderPlacedEvent> {

    @Autowired
    private MailService mailService;

    @Override
    public void onApplicationEvent(OrderPlacedEvent event) {
        Order order = event.getOrder();
        // 发送订单确认邮件
        mailService.sendOrderConfirmationEmail(order);
        // 这里还可以进行积分更新或其他操作
    }
}
  1. 在处理下单流程的服务中,下单成功后发布事件。
@Service
public class OrderProcessingService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void processOrder(Order order) {
        // ... 处理订单的核心逻辑 ...
        // 订单成功创建后,发布事件
        eventPublisher.publishEvent(new OrderPlacedEvent(order));
    }
}

5.代码实现

上述例子已经展示了如何创建事件、监听器以及如何在服务中发布事件的基本结构。对于异步处理,可以在配置文件中设置SimpleApplicationEventMulticaster为异步模式,或者使用AsyncConfigurer来自定义异步事件处理器。

6.与消息队列的区别

尽管Spring Event提供了事件驱动的方式,但它并不具备消息队列(MQ)的功能特性,例如持久化、分布式、消息堆积、重试机制等。在高并发、分布式环境和需要保证消息可靠传递的场景下,通常会采用RabbitMQ、Kafka等消息中间件替代Spring Event进行消息传递。

与MQ的区别:
1.范围与架构层次:Spring Event主要应用于单个应用内部,而MQ(如RabbitMQ、RocketMQ、Kafka等)适用于分布式系统间的消息传递,跨越多个独立运行的应用实例或微服务。

2.持久化与可靠性:Spring Event默认是非持久化的,消息一旦未被监听器消费,则可能丢失。相比之下,MQ通常支持消息持久化存储和可靠传输,即使消费者暂时不可用,消息也能在之后恢复时重新消费。

3.扩展性与性能:MQ通常提供集群部署和高可用保障,能够水平扩展以处理大量并发消息。而Spring Event的扩展性取决于应用本身的部署架构,难以应对大规模高并发场景。


原文地址:https://blog.csdn.net/m0_37583655/article/details/136151910

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!