Spring Boot 事件监听机制工作原理

前言:

我们知道在 Spring 、Spring Boot 的启动源码中都大量的使用了事件监听机制,也就是我们说的的监听器,监听器的实现基于观察者模式,也就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦,但如果想要实现系统层面的解耦,那么就要使用消息队列了,本篇从详细分析一下监听器的原理。

Spring Boot 系列文章传送门

Spring Boot 启动流程源码分析(2)

Spring Boot 启动流程源码分析(2)

Spring Boot 自动配置实现原理(源码分析)

Spring Boot 自定义 starter 启动器

事件监听器的核心元素

  • 事件(ApplicationEvent):监听器触发的原因,当事件源发生了某个事件,对应的监听器就会被触发,Spring 中场常见的事件 ContextRefreshEvent、ContextRStartEvent、ContextStoppedEvent、ContextCloseEvent、ReqyestHandlerEvent 等事件。
  • 监听器(ApplicationListener):监听特定事件,并在事件内部定义了事件发生后的响应逻辑,对应观察者模式的观察者。
  • 事件发布器(ApplicationEventMulticaster):负责发布事件,维护事件和事件监听器之间的关系,并在事件发生时通知相关监听器,对应观察者模式中的被观察者。

监听器的工作流程

  1. 事件监听器注册到事件发起器,用于监听事件。
  2. 事件源产生事件,然后像发布器发布事件。
  3. 事件发布器回调事件监听器的回调方法。
  4. 事件监听器的回调方法被调用,执行业务。

事件发布器的初始化时机

事件发布器又叫事件多播器,我们分析了事件监听器的工作流程,其中事件监听器是要往事件发布器中注册的,那意味着事件监听器开始注册之前已经有了事件发布器,那事件发布器是什么时候初始化的呢?事件发布器是在 AbstractApplicationContext#refresh 方法中调用了 AbstractApplicationContext#registerListeners 方法,完成事件发布器的初始化,如下:

//初始化一个事件多播器
protected void initApplicationEventMulticaster() {//获取 beanFactoryConfigurableListableBeanFactory beanFactory = this.getBeanFactory();//beanFactory 中是否有 应用程序事件多播器if (beanFactory.containsLocalBean("applicationEventMulticaster")) {//有直接赋值this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}} else {//没有就创建一个 应用程序事件多播器this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);//应用程序事件多播器 注册到 beanFactory 中beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);if (this.logger.isTraceEnabled()) {this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}}

事件监听器的注册时机

事件监听器需要注册到事件发布器中,那事件监听器是设么时候注册的?监听器的注册是在 Spring 容器刷新的时候完成的,AbstractApplicationContext#refresh 方法中调用了 AbstractApplicationContext#registerListeners 方法完成注册,如下:

//注册监听器
protected void registerListeners() {//准备遍历监听器Iterator var1 = this.getApplicationListeners().iterator();while(var1.hasNext()) {ApplicationListener<?> listener = (ApplicationListener)var1.next();//往 应用程序事件多播器 中添加监听器this.getApplicationEventMulticaster().addApplicationListener(listener);}//获取所有监听器String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);String[] var7 = listenerBeanNames;int var3 = listenerBeanNames.length;for(int var4 = 0; var4 < var3; ++var4) {String listenerBeanName = var7[var4];//往 应用程序事件多播器 中添加监听器this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}//早期要处理的事件Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {//有早期需要处理的事件Iterator var9 = earlyEventsToProcess.iterator();//遍历派发出去while(var9.hasNext()) {ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();//使用 应用程序事件多播器 将事件派发出去  重点关注 multicastEvent 方法this.getApplicationEventMulticaster().multicastEvent(earlyEvent);}}}

事件监听器在完成事件注册的时候,最后会调用 SimpleApplicationEventMulticaster#multicastEvent 方法(Spring Boot 源码中也会调用这段方法)发布事件,如下:

//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
public void multicastEvent(ApplicationEvent event) {//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEventthis.multicastEvent(event, this.resolveDefaultEventType(event));
}//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {//解析事件类型ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);//获取线程池Executor executor = this.getTaskExecutor();//迭代遍历监听器Iterator var5 = this.getApplicationListeners(event, type).iterator();while(var5.hasNext()) {//监听器ApplicationListener<?> listener = (ApplicationListener)var5.next();if (executor != null) {//线程池异步发送监听事件executor.execute(() -> {this.invokeListener(listener, event);});} else {//同步发送监听事件this.invokeListener(listener, event);}}}

SimpleApplicationEventMulticaster#multicastEvent 方法中可以发布异步事件,如果事件发布器有线程池就可以发布异步事件。

事件发布器发布完成事件后又是怎么完成事件回调的?

前面我们在分析事件发布的时候最后调用了 SimpleApplicationEventMulticaster#multicastEvent 方法,SimpleApplicationEventMulticaster#multicastEvent 方法中又调用了 SimpleApplicationEventMulticaster#invokeListener 方法,如下:

//org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {//获取 ErrorHandlerErrorHandler errorHandler = this.getErrorHandler();//ErrorHandler  为空判断if (errorHandler != null) {try {//不为空 调用 doInvokeListener 如果有异常 调用errorHandler 来处理this.doInvokeListener(listener, event);} catch (Throwable var5) {errorHandler.handleError(var5);}} else {//没有 errorHandler this.doInvokeListener(listener, event);}}//org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {//执行监听器方法listener.onApplicationEvent(event);} catch (ClassCastException var6) {String msg = var6.getMessage();if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {throw var6;}Log logger = LogFactory.getLog(this.getClass());if (logger.isTraceEnabled()) {logger.trace("Non-matching event type for listener: " + listener, var6);}}}@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E var1);
}

SimpleApplicationEventMulticaster#invokeListener 方法会对是否有 ErrorHandler 进行不同的处理,但最终都会调用 SimpleApplicationEventMulticaster#doInvokeListener 方法,完成监听器方法的调用。

Spring Boot 是如何完成事件监听的?

Spring Boot 在启动时候调用了 SpringApplication#run 方法,SpringApplication#run 方法中有如下两行代码:

//获取 SpringApplicationRunListener 实例数组 默认获取的是 EventPublishRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//启动监听 重点关注
listeners.starting(bootstrapContext, this.mainApplicationClass);

我们接着分析 SpringApplicationRunListeners#starting 方法。

SpringApplicationRunListeners#starting 方法源码分析

SpringApplicationRunListeners#starting 方法最终调用了 SimpleApplicationEventMulticaster#multicastEvent 方法,这个方法我们刚刚在上面分析了的,至此回到了 Spring 的方法,Spring Boot 和 Spring 事件监听机制原理基本相同。

//org.springframework.boot.SpringApplicationRunListeners#starting
void starting() {//迭代遍历所有监听器Iterator var1 = this.listeners.iterator();while(var1.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();//调用 事件发布运行监听器 EventPublishingRunListener#starting 方法listener.starting();}}//org.springframework.boot.context.event.EventPublishingRunListener#starting
public void starting() {//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEvent 多播事件 也就是启动监听事件this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

Spring Boot 中事件发布器何时完成初始化的?

我们知道 Spring 中事件发布器是在 AbstractApplicationContext#refresh 方法中完成初始化的的,刚刚上面分析 Spring Boot 的事件发布流程中似乎没有时间发布器的初始化操作,那 Spring Boot 事件发布器是何时初始化的?请看如下代码:

//org.springframework.boot.SpringApplicationRunListeners#starting
void starting() {//迭代遍历所有监听器Iterator var1 = this.listeners.iterator();while(var1.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();//调用 事件发布运行监听器 EventPublishingRunListener#starting 方法listener.starting();}}//org.springframework.boot.context.event.EventPublishingRunListener#starting
public void starting() {//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEvent 多播事件 也就是启动监听事件this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

EventPublishingRunListener#starting 方法中直接使用了 SimpleApplicationEventMulticaster#multicastEvent 发布事件,SimpleApplicationEventMulticaster 作为 EventPublishingRunListener 类的一个属性,在创建 EventPublishingRunListener 对象时候已经初始化了

EventPublishingRunListener 类源码分析

EventPublishingRunListener 这里只是看下构造方法,事件发布器 SimpleApplicationEventMulticaster 作为 EventPublishingRunListener 的一个属性,在 EventPublishingRunListener 完成创建的时候已经初始化了,而 EventPublishingRunListener 又是通过 Spring Boot 自动注入完成创建的。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {private final SpringApplication application;private final String[] args;private final SimpleApplicationEventMulticaster initialMulticaster;public EventPublishingRunListener(SpringApplication application, String[] args) {this.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();Iterator var3 = application.getListeners().iterator();while(var3.hasNext()) {ApplicationListener<?> listener = (ApplicationListener)var3.next();this.initialMulticaster.addApplicationListener(listener);}}//省略部分代码。。。。
}

总结:Spring Boot 事件监听机制工作原理其实和 Spring 的事件监听机制原理一样,Spring Boot 只是通过自动配置简化了一些复杂的配置而已,如果我们熟读 Spring 的源码,那对 Spring Boot 的源码解读是有极大帮助的,Spring Boot 很多功能都是基于 Spring 的,最后本篇关于 Spring Boot 事件监听机制工作原理的分享,希望可以帮助到需要的小伙伴。

如有不正确的地方请各位指出纠正。

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

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

相关文章

SAP FICO自定义权限对象及自定义作业创建

设置的通用说明 要求设置税收分组权限&#xff0c;自定义权限对象&#xff1a;Z_SSFZ 执行按钮权限控制&#xff1a;权限对象Z_SSFZ 字段名&#xff1a;ZSSFZ对应维护税收分组 字段名&#xff1a;ZACTVT01 01&#xff1a;付款银行信息维护 02&#xff1a;员工基本信息维护…

Ant-Vue——modal对话框

在Vue3的项目里&#xff0c;组件库用的是Ant-design 3.2版本 修改样式 需求是需要修改原生的 modal 样式 如果直接用类名 .ant-modal-body 去修改样式&#xff0c;会发现无法修改。因为默认不支持修改高度和外边距padding 所以需要&#xff1a;先通过 挂载元素 再css穿透/de…

知识图谱驱动的深度推理:ToG算法的创新与应用

LLMs通过预训练技术在大量文本语料库上生成连贯且符合上下文的响应。然而&#xff0c;面对需要复杂知识推理的任务时&#xff0c;它们存在明显的局限性。这些问题包括对超出预训练阶段的专业知识的准确回答失败&#xff0c;以及缺乏责任性、可解释性和透明度。为了解决这些问题…

伦敦银交易平台价格的突破成不成功?这点很重要!

在伦敦银交易中&#xff0c;当银价出现突破的时候&#xff0c;也正是引起很多投资者关注的时候。一旦银价出现突破&#xff0c;很可能是新行情的开端。但是做过突破交易&#xff0c;有相关经验的朋友会发现&#xff0c;自己在伦敦银交易平台做突破的时候&#xff0c;也并不是每…

【QT】多元素控件

多元素控件 多元素控件1. List Widget2. Table Widget3. Tree Widget 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别&#xff0c;以 QTableWidget 和 QTableView 为例&#xff1a; …

怎麼解決代理伺服器中的網路延遲問題-okeyproxy

使用代理伺服器時&#xff0c;不可避免地會遇到網路延遲的問題。本文將深入探討代理伺服器中的網路延遲問題&#xff0c;並提出一些解決方案。 什麼是網路延遲&#xff1f; 網路延遲指的是數據從一個點傳輸到另一個點所需的時間&#xff0c;它通常以毫秒&#xff08;ms&#…

数据结构--二叉树相关题2(OJ)

1.比较对称二叉树&#xff08;镜像二叉树&#xff09; 二叉树相关题1中第二题的变形题。先去看1哦&#xff01; 左子树和右子树比较 bool _isSymmetric(struct TreeNode* p, struct TreeNode* q) {if (p NULL && q NULL)return true;//如果两个都为空则是相等的if …

01前端导入

【一】前后端介绍 1.前端&#xff1a;通俗的说就是用户所看到的页面&#xff0c;通过浏览器渲染出来的页面 2.后端&#xff1a;就是对数据进行逻辑校验和加密等操作&#xff0c;用户看不到的操作 【二】浏览器访问地址全流程 1.输入网址--》https://www.baidu.com/域名 2.解…

AI产品经理一篇读透知识图谱(万字深度好文)

1、知识图谱技术综述 知识图谱技术是人工智能技术的组成部分&#xff0c;其强大的语义处理和互联组织能力&#xff0c;为智能化信息应用提供了基础。以下内容涵盖了基本定义与架构、代表性知识图谱库、构建技术、开源库和典型应用。 引言 随着互联网的发展&#xff0c;网络数…

查询进程并且杀死

使用命令行工具 Tasklist 和 Taskkill 来查询和删除&#xff08;终止&#xff09;进程。以下是详细步骤&#xff1a; 查询进程 打开命令提示符&#xff08;以管理员身份运行以获得所有权限&#xff09;。 使用 tasklist 命令列出所有正在运行的进程&#xff1a; tasklist这将…

抖音短视频矩阵系统全攻略:从入门到精通

在数字化时代&#xff0c;短视频已成为连接用户与内容的桥梁。抖音作为短视频领域的领军平台&#xff0c;其影响力不容小觑。抖音短视频矩阵系统的构建和管理&#xff0c;对于希望在这个平台上取得成功的创作者和品牌来说至关重要。本文将提供一份全面的攻略&#xff0c;从入门…

Python|Pyppeteer实现获取携程网“指定城市”特价机票(25)

前言 本文是该专栏的第25篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 本文以携程的城市特价机票为例子,通过pyppeteer登录携程,然后指定两个目标城市(出发城市,目的地到达城市),获取这两个城市之间的特价机票。 需要注意的是,对pyppeteer不太熟悉的…

Qt篇——QLabel固定尺寸的情况下让字体大小自适应并自动换行以完整显示

当文字较少时&#xff0c;默认字体大小为16&#xff1b;当文字内容较多时&#xff0c;自动换行并缩小字体。 举例&#xff1a; 字体较少时 字体较多时 思路&#xff1a; 设置自动换行属性 setWordWrap&#xff1b;通过QFontMetrics计算文字字体要多大、显示多少行才不会超过…

做外贸干一行爱一行,还是干一行厌一行?

记得年轻的时候&#xff0c;每每和同龄人不同行业聊天的时候&#xff0c;大家普遍的感觉就是&#xff1a;自己这一行太苦了&#xff0c;以后有孩子了干什么都不能让他做自己这一行。 和在银行上班的同学聊天&#xff0c;他们最大的苦恼是需要每天开发客户&#xff0c; 让客户在…

【0295】Posgres内核 dynahash table 之 hash_search 实现原理(2)

相关文章: 【0294】Postgres内核 dynahash 之 hash_search 实现原理(1) 0. 前言 在【0294】Postgres内核 dynahash 之 hash_search 实现原理(1) 一文中,从Postgres内核源码角度详细讲解了dynamic hash table中 hash search 的实现原理。 具体内容: 如何确定segment位置…

8、开发与大模型对话的独立语音设备

一、设计原理 该系统的核心部分主要由ESP32-WROVER开发板和ESP32-CAM摄像头、MAX9814麦克风放大器模块、MAX98357功放、声音传感器和SU-03T语音识别芯片构成。通过使用ESP32-WROVER开发板,用户可以实现通过语音与ai进行交互并进行人脸识别。 系统中,从外部输入电源中获取电源…

一手洞悉泰国slot线上游戏投放本土网盟CPI计费广告优势

一手洞悉泰国slot线上游戏投放本土网盟CPI计费广告优势 ​在泰国这个拥有独特文化背景和审美观念的国家&#xff0c;Slots游戏以其丰富的玩法和刺激的体验迅速赢得了玩家们的喜爱。然而&#xff0c;要在竞争激烈的市场中脱颖而出&#xff0c;有效的推广策略显得尤为重要。本土…

HTTP有哪些失败原因?怎么处理?

在网络通信中&#xff0c;HTTP&#xff08;超文本传输协议&#xff09;是最常用的协议之一&#xff0c;它支持万维网上的数据交换。然而&#xff0c;在使用HTTP进行数据传输时&#xff0c;可能会遇到各种失败的情况。本文将探讨HTTP请求失败的常见原因&#xff0c;并提供相应的…

【Excel操作】Python Pandas判断Excel单元格中数值是否为空

判断Excel单元格中数值是为空&#xff0c;主要有下面两种方法&#xff1a; 1. pandas.isnull 2. pandas.isna判断Excel不为空&#xff0c;也有下面两种方法&#xff1a; 1. pandas.notna 2. pandas.notnull假设有这样一张Excel的表格 我们来识别出为空的单元格 import panda…

Python酷库之旅-第三方库Pandas(010)

目录 一、用法精讲 22、pandas.read_hdf函数 22-1、语法 22-2、参数 22-3、功能 22-4、返回值 22-5、说明 22-6、用法 22-6-1、数据准备 22-6-2、代码示例 22-6-3、结果输出 23、pandas.HDFStore.put方法 23-1、语法 23-2、参数 23-3、功能 23-4、返回值 23-5…