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() {
    //获取 beanFactory
    ConfigurableListableBeanFactory 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#multicastEvent
	this.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) {
	//获取 ErrorHandler
	ErrorHandler 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.mfbz.cn/a/766769.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电子价签能够给零售业带来哪些效益?

在竞争激烈的零售市场中&#xff0c;每一个细微的优化都可能成为吸引顾客和提升效率的关键。随着技术的不断进步&#xff0c;电子价签作为一种革新性的解决方案&#xff0c;正以其独特的优势重新定义零售运营的标准。那它到底能给我们的零售门店带来哪些实际效益&#xff1f; …

Qt时间日期处理与定时器使用总结

一、日期时间数据 1.QTime 用于存储和操作时间数据的类&#xff0c;其中包括小时(h)、分钟(m)、秒(s)、毫秒(ms)。函数定义如下&#xff1a; //注&#xff1a;秒(s)和毫秒(ms)有默认值0 QTime::QTime(int h, int m, int s 0, int ms 0) 若无须初始化时间数据&#xff0c;可…

基于FPGA的DDS信号发生器

前言 此处仅为基于Vivado实现DDS信号发生器的仿真实现&#xff0c;Vivado的安装请看下面的文章&#xff0c;这里我只是安装了一个标准版本&#xff0c;只要能够仿真波形即可。 FPGA开发Vivado安装教程_vivado安装 csdn-CSDN博客 DDS原理 DDS技术是一种通过数字计算生成波形…

Linux shell编程学习笔记61: pstree 命令——显示进程树

0 前言 在 Linux shell编程学习笔记59&#xff1a; ps 获取系统进程信息&#xff0c;类似于Windows系统中的tasklist 命令https://blog.csdn.net/Purpleendurer/article/details/139696466?spm1001.2014.3001.5501 中我们研究了ps命令。在Linux中&#xff0c;通过ps命令&am…

Perl语言入门指南

一、绪论 1.1 Perl语言概述 1.2 Perl的特色 1.3 Perl面临的问题 1.4 Perl语言的应用领域 二、Perl语言基础 2.1 Perl语言的历史发展 2.2 Perl语言的基本语法 2.3 Perl语言的数据类型 三、Perl语言控制结构 3.1 条件语句 3.2 循环结构 3.3 函数和子程序 四、Perl语…

RK3568驱动指南|第十五篇 I2C-第183章 SMBus总线介绍

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

电脑版微信自动发送微信消息给好友或者群聊

一.软件下载 点击微信自动发送消息下载 二.相关使用方法 1.打开软件&#xff0c;输入想自动发送的内容 2.确保登录了微信电脑版【PC端】&#xff0c;然后切换到想要自动发送的好友或群聊的窗口。 3.点击开始&#xff0c;现在自动发送即可&#xff0c;稍等三秒程序自动运行。 …

小程序开发平台版源码系统——万能门店小程序功能 前后端分离 带完整的安装代码包以及搭建教程

系统概述 在移动互联网的浪潮中&#xff0c;小程序以其轻量、便捷、无需下载即可使用的特点&#xff0c;迅速成为连接用户与商家的新桥梁。为了满足广大商家快速搭建个性化、高效运营的小程序需求&#xff0c;我们精心打造了“小程序开发平台版源码系统——万能门店小程序功能…

秋招——MySQL补充——MySQL是如何加行级锁

文章目录 引言正文什么SQL语句会加行级锁查询操作增加对应的行级锁事务的写法 update和delete修改操作也会增加行级锁 行级锁有哪些种类记录锁间隙锁Next-Key锁 MySQL是如何加行级锁&#xff1f;唯一索引等值查询查询记录是存在的查询记录是不存在的 唯一索引范围查找针对大于或…

【python脚本】批量检测sql延时注入

文章目录 前言批量检测sql延时注入工作原理脚本演示 前言 SQL延时注入是一种在Web应用程序中利用SQL注入漏洞的技术&#xff0c;当传统的基于错误信息或数据回显的注入方法不可行时&#xff0c;例如当Web应用进行了安全配置&#xff0c;不显示任何错误信息或敏感数据时&#x…

Element中的消息提示组件Message和弹框组件MessageBox

简述&#xff1a;在 Element UI 中&#xff0c;Message和MessageBox都是比较常用的组件&#xff0c;Message用来提示消息&#xff0c;而MessageBox是一个用于创建模态对话框的组件。它可以用于在页面上快速展示信息、警告或错误提示&#xff0c;而不会阻止用户的其他操作。简单…

Pandas_DataFrame读写详解:案例解析(第24天)

系列文章目录 一、 读写文件数据 二、df查询数据操作 三、df增加列操作 四、df删除行列操作 五、df数据去重操作 六、df数据修改操作 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、 读写文…

华为DCN之:SDN和NFV

1. SDN概述 1.1 SDN的起源 SDN&#xff08;Software Defined Network&#xff09;即软件定义网络。是由斯坦福大学Clean Slate研究组提出的一种新型网络创新架构。其核心理念通过将网络设备控制平面与数据平面分离&#xff0c;从而实现了网络控制平面的集中控制&#xff0c;为…

深入理解C++中的锁

目录 1.基本互斥锁&#xff08;std::mutex&#xff09; 2.递归互斥锁&#xff08;std::recursive_mutex&#xff09; 3.带超时机制的互斥锁&#xff08;std::timed_mutex&#xff09; 4.带超时机制的递归互斥锁&#xff08;std::recursive_timed_mutex&#xff09; 5.共享…

图解 Kafka 架构

写在前面 Kafka 是一个可横向扩展&#xff0c;高可靠的实时消息中间件&#xff0c;常用于服务解耦、流量削峰。 好像是 LinkedIn 团队开发的&#xff0c;后面捐赠给apache基金会了。 kafka 总体架构图 Producer&#xff1a;生产者&#xff0c;消息的产生者&#xff0c;是消息的…

android AIDL使用demo

背景 最近打算学习一下如何在framework层添加一个自定义service。 了解到自定义service需要使用aidl&#xff0c;为了加强对aidl的了解和使用过程&#xff0c;特意又温习了一下aidl的使用&#xff0c;并用博客的形式记录下来。 aidl官方参考&#xff1a;https://developer.and…

不同系统间数据交换要通过 api 不能直接数据库访问

很多大数据开发提供数据给外部系统直接给表结构&#xff0c;这是不好的方式。在不同系统间进行数据交换时&#xff0c;通过API&#xff08;应用程序编程接口&#xff09;而非直接访问数据库是现代系统集成的一种最佳实践。 目录 为什么要通过API进行数据交换如何通过API进行数据…

论文辅导 | 基于多尺度分解的LSTM⁃ARIMA锂电池寿命预测

辅导文章 模型描述 锂电池剩余使用寿命&#xff08;Remaining useful life&#xff0c;RUL&#xff09;预测是锂电池研究的一个重要方向&#xff0c;通过对RUL的准确预测&#xff0c;可以更好地管理和维护电池&#xff0c;延长电池使用寿命。为了能够准确预测锂电池的RUL&…

STM32 看门狗 HAL

由时钟图可以看出看门狗采用的是内部低速时钟&#xff0c;频率为40KHz 打开看门狗&#xff0c;采用32分频&#xff0c;计数1250。 结合设置的分频系数和重载计数值&#xff0c;我们可以计算出看门狗的定时时间&#xff1a; 32*1250/40kHz 1s 主函数中喂狗就行 HAL_IWDG_Ref…

STM32 HAL库读取ID

在stm32f1xx_hal.c文件中由读取ID号的子函数&#xff0c;不同单片机的UID_BASE不同&#xff0c;本单片机用的是STM32F103CBT6,跳转之后可以看到地址为&#xff1a;0x1FFFF7E8 在程序中只需定义一个数组调用读取ID的函数即可 uint32_t UID[3]; while(1) { UID[0] HAL_GetUIDw0…