Spring Event详解

Spring Event详解

  • 1.详细介绍
  • 2.使用场景
  • 3.注意事项
  • 4.案例分析
  • 5.代码实现
  • 6.与消息队列的区别
  • 7.SpringEvent结合线程池异步实现

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> {@Autowiredprivate MailService mailService;@Overridepublic void onApplicationEvent(OrderPlacedEvent event) {Order order = event.getOrder();// 发送订单确认邮件mailService.sendOrderConfirmationEmail(order);// 这里还可以进行积分更新或其他操作}
}
  1. 在处理下单流程的服务中,下单成功后发布事件。
@Service
public class OrderProcessingService {@Autowiredprivate 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的扩展性取决于应用本身的部署架构,难以应对大规模高并发场景。

7.SpringEvent结合线程池异步实现

在Spring框架中,为了实现异步处理事件,可以结合线程池使用TaskExecutor来配置ApplicationEventMulticaster以发布事件到监听器。以下是如何结合线程池实现Spring Event异步发布的步骤和示例:

  1. 首先,在Spring配置类中定义一个线程池任务执行器(如ThreadPoolTaskExecutor)。
@Configuration
public class AsyncConfig {@Beanpublic ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 根据实际情况设置线程池参数executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(20);executor.setThreadNamePrefix("event-");executor.initialize();return executor;}
}
  1. 接下来,配置SimpleApplicationEventMulticaster以使用上述线程池执行器进行异步事件分发。
@Configuration
public class EventConfig {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;@Beanpublic SimpleApplicationEventMulticaster applicationEventMulticaster() {SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();multicaster.setTaskExecutor(taskExecutor);return multicaster;}
}
  1. 现在,当你通过ApplicationEventPublisher发布事件时,它们将由配置好的线程池异步处理。
@Service
public class SomeService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void someMethodThatTriggersEvent() {// 业务逻辑...eventPublisher.publishEvent(new CustomEvent(this, "Some data"));}
}@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {// 异步处理事件的逻辑System.out.println("Received event: " + event.getData());}
}

这样,当SomeService中的方法触发事件时,CustomEventListener将会在一个独立的线程池线程上异步接收并处理该事件。这有助于提高应用性能,特别是对于耗时较长的事件处理任务。

在Spring框架中,CustomEvent通常是指自定义事件类,开发者可以根据业务需求创建一个继承自ApplicationEvent的类来表示特定的业务事件。例如:

import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {private String eventData;public CustomEvent(Object source, String eventData) {super(source);this.eventData = eventData;}public String getEventData() {return eventData;}
}

在这个例子中,CustomEvent是一个自定义事件类型,它包含了一个字符串类型的eventData属性,用于携带具体的业务数据。构造函数接受两个参数,第一个是事件源(source),通常会传入触发该事件的对象实例;第二个参数是自定义的数据信息。

然后,在应用中的其他地方可以注册一个监听这个自定义事件的监听器,实现ApplicationListener<CustomEvent>接口,并在onApplicationEvent()方法中处理该事件。

import org.springframework.context.ApplicationListener;@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("Received custom event with data: " + event.getEventData());// 在这里执行对CustomEvent的处理逻辑}
}

通过这种方式,当应用程序发布一个CustomEvent时,所有注册了该事件类型的监听器都会异步或同步地接收到通知并执行相应的处理逻辑。如果结合线程池进行异步处理,那么这些事件将在后台线程中被监听器消费,从而不会阻塞主线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/689732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

并查集,真好用,一次AC不是梦!

文章目录 &#x1f680;前言&#x1f680;并查集&#x1f680;并查集的两个优化✈️路径压缩✈️按秩合并 &#x1f680;并查集代码模板 &#x1f680;前言 大家好啊&#xff01;今天阿辉来给大家介绍一种简洁而优雅的数据结构——并查集&#xff0c;不知道各位是否了解它&…

ssh连接服务器需要子网掩码吗?

IP寻址需要同时知道IP地址和子网掩码&#xff0c;但是在通过ssh连接服务器时&#xff0c;只需要知道IP地址和端口号就可以了&#xff0c;ssh通讯为什么不需要子网掩码呢。在不知道子网掩码的前提下&#xff0c;可以正确找到IP对应的主机吗&#xff1f; 不需要&#xff0c;SSH&a…

桌面显示器应用Type-C接口

随着科技的飞速发展&#xff0c;桌面显示器作为我们日常工作中不可或缺的设备之一&#xff0c;也在不断地更新换代。其中&#xff0c;Type-C接口的应用成为了桌面显示器发展的一个重要趋势。那么&#xff0c;桌面显示器应用Type-C接口究竟有什么好处呢&#xff1f; 首先&#x…

职场隐私守则:关系再好也别碰这些“雷区”

在职场中&#xff0c;与同事建立良好的关系是非常重要的&#xff0c;它有助于提高工作效率、增进团队协作&#xff0c;并且能够为日常的工作带来便利。 然而&#xff0c;即便与同事的关系再亲密&#xff0c;也有一些隐私话题是绝对不能轻易透露的。 在与同事和领导相处时&…

文章复现 | 差异分析和PPI网络构建

原文链接&#xff1a;差异分析和PPI网路图绘制教程 写在前面 在原文中&#xff0c;作者获得285个DEG&#xff0c;在此推文中共获得601个DEG。小杜的猜想是标准化的水段不同的原因吧&#xff0c;或是其他的原因。此外&#xff0c;惊奇的发现发表医学类的文章在附件中都不提供相…

【微信小程序】wxss 和 css 、wxml 和 html 区别

wxss 和 css 区别 wxss 支持小程序特有的选择器和 样式属性 scroll-into-view cover-view 等wxss 引入了 rpx 单位&#xff0c;可以根据屏幕宽度进行自适应&#xff0c;使得开发者可以更方便的处理不同尺寸屏幕的适配问题。wxss 背景图片只能引入外链&#xff0c;不能使用本地…

golang 获取域名 ip dns 信息

1 Go语言查找DNS A记录 A (Address) 记录是用来指定主机名&#xff08;或域名&#xff09;对应的IP地址记录. 用户可以将该域名下的网站服务器指向到自己的web server上. 同时也可以设置您域名的二级域名. 使用 Go 语言的标准库 net.LookupIP() 接受域名的字符串参数,返回 ne…

【零基础学习CAPL】——CAN报文的发送(LiveCounter——生命信号)

🙋‍♂️【零基础学习CAPL】系列💁‍♂️点击跳转 文章目录 1.概述2.面板创建3.系统变量创建4.CAPL实现5.效果5.1.0~15循环发送5.2.固定值发送6.全量脚本1.概述 本章主要介绍带有生命信号LiveCounter的报文发送脚本 一般报文可使用CANoe的IG模块直接发送,但存在循环冗余…

代码随想录算法训练营第三十六天|435.无重叠区间、763.划分字母区间、56.合并区间

435.无重叠区间 思路&#xff1a;找到删除几个区间&#xff0c;让题目给出的区间没有重叠部分&#xff0c;那么首先我们先进行排序&#xff08;按照左边界排序&#xff09;&#xff0c;那么下一个区间的左边界小于上一个区间的右边界&#xff0c;那么这两个区间一定有重叠的部分…

燃气热水器水箱的气密性测试密封方案介绍—格雷希尔快速接头

家用燃气热水器的气密性检测是保障其安全性的重要环节之一&#xff0c;如热水器的水箱&#xff0c;它的周围缠绕着一圈铜管&#xff0c;这圈铜管和水箱之间有数量不等的焊接点&#xff0c;为了保证热水器的正常运行&#xff0c;必须要对它们进行气密性测试。   燃气热水器水箱…

文件且目录损坏无法读取怎么办?

文件及目录损坏无法读取是计算机使用过程中的常见故障&#xff0c;这可能是由于多种原因导致的&#xff0c;例如硬件故障、文件系统错误、病毒感染或不当操作等。本文将对这一问题进行深入分析&#xff0c;探讨其根本原因&#xff0c;并提供相应的解决方法&#xff0c;包括数据…

Gemini API模型详细信息一览表

Gemini API模型详细信息一览表&#xff0c;api模型&#xff1a;gemini-pro\gemini-pro-vision\embedding-001\aqa VariationAttributeDescriptionGemini ProModel last updatedFebruary 2024Model codemodels/gemini-proModel capabilities Input: textOutput: textGenerates …

ubuntu linux kernel内核操作

替换linux内核 实验步骤(在root权限下进行) 1.内核编译前的准备工作 2.下载内核 4. 编译新内核 5. 内核安装 6. 安装模块 7. 生成initrd.img文件 8. 切换到/boot/grub/目录下,自动查找新内核,并添加至grub引导 9. 重启Ubantu,在previous version中选择启动新编译的内核 …

Collectors.toMap使用记录

最近看到一行比较有趣的代码&#xff0c;他的功能就是将List转换为Map。我觉得挺有意思的&#xff0c;先前没遇到过&#xff0c;于是打算写篇文章记录一下。 public enum CodeEnum {USER("user"),AGE("age"),INTEREST("interest"),;private Str…

揭秘:一行代码搞定.Net API高并发的烦恼

高并发下的接口请求重复提交问题 在.Net开发中&#xff0c;我们经常遇到用户疯狂点击同一按钮&#xff0c;或者服务响应慢时重复发送请求&#xff0c;导致数据重复添加或混乱。这不仅浪费资源&#xff0c;更会得到错误的业务结果。如何高效解决这一普遍问题呢&#xff1f; 常规…

vscode 命令无法执行

以管理员启动powershell 输入set-ExecutionPolicy RemoteSigned yarn : 无法加载文件 D:\node\node_global\yarn.ps1。未对文件 D:\node\node_global\yarn.ps1 进行数字签名。无法在当前系统上运行该脚本。有关运行脚本和设置执行策略的详细信息&#xff0c;请参阅 https:/go…

2024年华为OD机试真题-计算三叉搜索树的高度-Java-OD统一考试(C卷)

题目描述: 定义构造三叉搜索树规则如下: 每个节点都存有一个数,当插入一个新的数时,从根节点向下寻找,直到找到一个合适的空节点插入。 查找的规则是: 1. 如果数小于节点的数减去500,则将数插入节点的左子树 2. 如果数大于节点的数加上500,则将…

影响效果图渲染的因素有哪些?渲染100邀请码1a12

效果图对很多行业是刚需&#xff0c;特别是视觉行业&#xff0c;一张好的效果图非常重要&#xff0c;影响效果图渲染的因素有很多&#xff0c;它们是哪些呢&#xff1f;一起来看看吧。 1、渲染引擎 渲染引擎对效果图的渲染至关重要&#xff0c;目前市面上有很多渲染引擎&#x…

阅读笔记(SOFT COMPUTING 2018)Seam elimination based on Curvelet for image stitching

参考文献&#xff1a; Wang Z, Yang Z. Seam elimination based on Curvelet for image stitching[J]. Soft Computing, 2018: 1-16. 注&#xff1a;SOFT COMPUTING 大类学科小类学科Top期刊综述期刊工程技术 3区 COMPUTER SCIENCE, ARTIFICIAL INTELLIGENCE 计算机&#xf…

6.1.0 MPLS LDP动态分配

手动写静态MPLS的转发路径&#xff0c;不仅不方便管理&#xff0c;同时配置量也非常多&#xff0c;每台设备都需要去做配置。为了方便管理且去除繁琐的配置&#xff0c;就需要使用到LDP动态标签分发协议了。 标签分发协议LDP&#xff08;Label Distribution Protocol&#xff…