【Spring Boot 源码学习】ApplicationListener 详解
Spring Boot 源码学习系列
ApplicationListener 详解
- 引言
- 往期内容
- 主要内容
- 1. 初识 ApplicationListener
- 2. 加载 ApplicationListener
- 3. 响应应用程序事件
- 总结
引言
书接前文《初识 SpringApplication》,我们从 Spring Boot 的启动类 SpringApplication
上入手,了解了 SpringApplication
实例化过程。其中,《BootstrapRegistryInitializer 详解》 和 《ApplicationContextInitializer 详解》博文中,Huazie 已经带大家详细分析了 BootstrapRegistryInitializer
和 ApplicationContextInitializer
的加载和初始化过程,如下还有 2.5 还未详细分析:
那本篇博文就主要围绕 2.5 的内容展开,详细分析一下 ApplicationListener
的加载和处理应用程序事件的逻辑。
往期内容
在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:
Spring Boot 源码学习 |
Spring Boot 项目介绍 |
Spring Boot 核心运行原理介绍 |
【Spring Boot 源码学习】@EnableAutoConfiguration 注解 |
【Spring Boot 源码学习】@SpringBootApplication 注解 |
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector |
【Spring Boot 源码学习】自动装配流程源码解析(上) |
【Spring Boot 源码学习】自动装配流程源码解析(下) |
【Spring Boot 源码学习】深入 FilteringSpringBootCondition |
【Spring Boot 源码学习】OnClassCondition 详解 |
【Spring Boot 源码学习】OnBeanCondition 详解 |
【Spring Boot 源码学习】OnWebApplicationCondition 详解 |
【Spring Boot 源码学习】@Conditional 条件注解 |
【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解 |
【Spring Boot 源码学习】RedisAutoConfiguration 详解 |
【Spring Boot 源码学习】JedisConnectionConfiguration 详解 |
【Spring Boot 源码学习】初识 SpringApplication |
【Spring Boot 源码学习】Banner 信息打印流程 |
【Spring Boot 源码学习】自定义 Banner 信息打印 |
【Spring Boot 源码学习】BootstrapRegistryInitializer 详解 |
【Spring Boot 源码学习】ApplicationContextInitializer 详解 |
主要内容
注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。
1. 初识 ApplicationListener
我们先来看看 ApplicationListener
接口的源码【spring-context-5.3.25.jar】:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {return event -> consumer.accept(event.getPayload());}
}
从上述代码,我们可以看到 ApplicationListener
接口被 @FunctionalInterface
注解修饰。
知识点:
@FunctionalInterface
是 Java 8 中引入的一个注解,用于标识一个函数式接口。函数式接口是只有一个抽象方法的接口,常用于实现 Lambda 表达式和方法引用。
使用@FunctionalInterface
注解可以向编译器指示该接口是一个函数式接口,从而在编译时进行类型检查,确保该接口 只包含一个抽象方法。此外,该注解还可以为函数式接口生成特殊的方法,如默认方法(default method)和 静态方法(static method),这些方法可以在接口中提供更多的功能,这里就不赘述了,感兴趣的朋友可以自行查阅相关函数式接口的资料。
ApplicationListener
是 Spring 中应用程序事件监听器实现的接口。它基于观察者设计模式的java.util.EventListener
接口的标准。在注册到 Spring ApplicationContext 时,事件将进行相应的过滤,只有匹配的事件对象才会使该监听器被调用。
在 ApplicationListener
接口中,我们可以看到它定义了一个 onApplicationEvent(E event)
方法,当监听事件被触发时,onApplicationEvent
方法就会被调用执行。onApplicationEvent
方法一般用于处理应用程序事件,参数 event
为 ApplicationEvent
的子类,也就是具体要响应处理的各种类型的应用程序事件。例如,当某个特定事件发生时,你可能想要记录日志、更新数据库、发送电子邮件等等。
另外,ApplicationListener
接口还提供了一个静态方法 forPayload(Consumer<T> consumer)
,用于创建一个新的 ApplicationListener
实例。这个方法接受一个 Consumer<T>
类型的参数,这个参数是一个函数接口,它接受一个泛型参数 T
,并对其执行一些操作。通过这个方法,你可以将一个 Consumer
函数作为参数,然后返回一个对应的事件监听器。这个监听器会在事件发生时,调用 Consumer
函数处理事件的有效载荷【即事件中包含的有效信息或数据】。
2. 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
上述代码是 SpringApplication
的核心构造方法中的逻辑,它用于加载实现了 ApplicationListener
接口的监听器实例集合,并将该监听器实例集合设置到 SpringApplication
的 listeners
变量中。
private List<ApplicationContextInitializer<?>> initializers;
我们进入 getSpringFactoriesInstances
方法,查看如下:
我们看到了如下的代码 :
SpringFactoriesLoader.loadFactoryNames(type, classLoader);
这里是通过 SpringFactoriesLoader
类的 loadFactoryNames
方法来获取 META-INF/spring.factories
中配置 key 为 org.springframework.context.ApplicationListener
的数据;
我们以 spring-boot-autoconfigure-2.7.9.jar 为例:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
3. 响应应用程序事件
这里我们需要查看 SpringApplication
的 run(String... args)
方法,如下所示:
我们看上面的 SpringApplicationRunListeners
,其内的 listeners
变量是 SpringApplicationRunListener
接口的集合,如下所示:
而 SpringApplicationRunListener
接口的一个实现就是 EventPublishingRunListener
类,该类的作用就是根据 Spring Boot 程序启动过程的 不同阶段 发布对应的事件,然后由不同的实现 ApplicationListener
接口的应用程序监听器,来处理对应的事件【有关 SpringApplicationRunListener
监听器的内容,我们后续博文中会详细介绍,这里不展开了】。
如下图是 SpringApplicationRunListeners
类中的方法,它们分别对应了 Spring Boot 程序启动过程中要发布的不同阶段的事件的逻辑。
starting
:当run
方法第一次被执行时,该方法会立即被调用,可用于非常早期的初始化工作environmentPrepared
:当environment
准备完成,在ApplicationContext
创建之前,该方法被调用contextPrepared
:当ApplicationContext
构建完成,资源还未被加载时,该方法被调用contextLoaded
:当ApplicationContext
加载完成,未被刷新之前,该方法被调用started
:当ApplicationContext
刷新并启动之后,CommandLineRunner
和ApplicationRunner
未被调用之前,该方法被调用ready
:当所有准备工作就绪,run
方法执行完成之前,该方法被调用failed
:当应用程序出现错误时,该方法被调用
我们以 starting
方法的逻辑为例,看一下 ApplicationStartingEvent
事件发布并被处理的过程。
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});
}
我们继续看 doWithListeners
方法:
结合上面的截图,我们重点看下这行:
(listener) -> listener.starting(bootstrapContext)
这里时调用了 SpringApplicationRunListener
接口的 starting
方法:
这里的 multicastEvent
方法就是用来发布一个指定的应用程序事件,比如这里发布的就是 ApplicationStartingEvent
事件。
总结
本篇 Huazie 带大家详细分析了 ApplicationListener
的加载和处理应用程序事件,这对于后续的 SpringApplication
运行流程的理解至关重要。
相关文章:

【Spring Boot 源码学习】ApplicationListener 详解
Spring Boot 源码学习系列 ApplicationListener 详解 引言往期内容主要内容1. 初识 ApplicationListener2. 加载 ApplicationListener3. 响应应用程序事件 总结 引言 书接前文《初识 SpringApplication》,我们从 Spring Boot 的启动类 SpringApplication 上入手&am…...

HCIP---RSTP/MSTP
文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 STP协议虽然能够解决环路问题,但是收敛速度慢,影响了用户通信质量。IEEE于2001年发布的802.1w标准定义了快速生成树协议RSTP(Rapid Spanning-Tree Proto…...

探索开源游戏的乐趣与无限可能 | 开源专题 No.47
CleverRaven/Cataclysm-DDA Stars: 9.0k License: NOASSERTION Cataclysm:Dark Days Ahead 是一个回合制的生存游戏,设定在一个后启示录世界中。尽管有些人将其描述为 “僵尸游戏”,但 Cataclysm 远不止于此。在这个残酷、持久、程序生成的世…...

springboot_3.2_freemark_基础环境配置
springboot_3.2_freemark_基础环境配置 一、前言二、环境三、相关资料四、目标五、默认配置项六、构建springboot 3.2项目6.1 pom.xml 内容:6.2 启动类6.3 添加ftlh模板6.4 controller内容6.5 bootstrap.yml配置 七、总结 一、前言 FreeMarker 是一款模板引擎&…...

【MySQL】MySQL 在 Centos 7环境安装教程
文章目录 1.卸载不要的环境2.检查系统安装包3.获取mysql官方yum源4.安装mysql yum 源,对比前后yum源5.安装mysql服务6.查看配置文件和数据存储位置7.启动服务和查看启动服务8.登录9.配置my.cnf 1.卸载不要的环境 先检查是否有mariadb存在 ps ajx |grep mariadb如果…...

有病但合理的 ChatGPT 提示语
ChatGPT 面世一年多了,如何让大模型输出高质量内容,让提示词工程成了一门重要的学科。以下是一些有病但合理的提示词技巧,大部分经过论文证明,有效提高 ChatGPT 输出质量: 1️⃣ Take a deep breath. 深呼吸 ✨ 作用…...

this.$emit(‘update:isVisible‘, false)作用
这个写是不是很新颖,传父组件传值!这是什么鬼。。。 假设你有以下逻辑业务。在A页面弹出一个组件B,A组件里面使用B组件,是否展示B组件你使用的是baselineShow变量控制! <BaselineData :isVisible.sync"basel…...

CnetSDK .NET OCR Library SDK Crack
CnetSDK .NET OCR Library SDK Crack CnetSDK .NET OCR Library SDK 是一款高精度 .NET OCR 扫描仪软件,用于从图像中识别字符,如文本、手写和符号。该.NET OCR库软件采用Tesseract OCR引擎技术,将字符识别准确率提高高达99%。通过将 .NET OC…...

基于Solr的全文检索系统的实现与应用
文章目录 一、概念1、什么是Solr2、与Lucene的比较区别1)Lucene2)Solr 二、Solr的安装与配置1、Solr的下载2、Solr的文件夹结构3、运行环境4、Solr整合tomcat1)Solr Home与SolrCore2)整合步骤 5、Solr管理后台1)Dashbo…...

【rabbitMQ】rabbitMQ控制台模拟收发消息
目录 1.新建队列 2.交换机绑定队列 3.查看消息是否到达队列 总结: 1.新建队列 2.交换机绑定队列 点击amq.fonout 3.查看消息是否到达队列 总结: 生产者(publisher)发送消息,先到达交换机,再到队列&…...

Java NIO, IO 整理
NIO: IO多路复用: 参考: Redis(六)单线程I/O多路复用模型浅析_单线程多路复用-CSDN博客 Java NIO 详解_java nio详解_开发菜鸡的博客-CSDN博客 Java Socket 之 NIO - 掘金 答应我,这次搞懂 I/O 多路复用!_小林coding的博客-CS…...

【数据结构】——排序篇(下)
前言:前面我们的排序已经详细的讲解了一系列的方法,那么我们现在久之后一个归并排序了,所以我们现在就来讲解一下归并排序。 归并排序: 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法…...

C++ 模拟实现vector
目录 一、定义 二、模拟实现 1、无参初始化 2、size&capacity 3、reserve 4、push_back 5、迭代器 6、empty 7、pop_back 8、operator[ ] 9、resize 10、insert 迭代器失效问题 11、erase 12、带参初始化 13、迭代器初始化 14、析构函数 完整版代码 一、…...

基于hadoop下的spark安装
目录 简介 安装准备 spark安装 配置文件配置 简介 Spark主要⽤于⼤数据的并⾏计算,⽽Hadoop在企业主要⽤于⼤数据的存储(⽐如HDFS、Hive和HBase 等),以及资源调度(Yarn)。但是也有很多公司也在使⽤MR2进…...

面试经典150题(10-13)
leetcode 150道题 计划花两个月时候刷完,今天(第四天)完成了4道(10-13)150: 10. (45. 跳跃游戏 II)题目描述: 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[…...

Sql server数据库数据查询
请查询学生信息表的所有记录。 答:查询所需的代码如下: USE 学生管理数据库 GO SELECT * FROM 学生信息表 执行结果如下: 查询学生的学号、姓名和性别。 答:查询所需的代码如下: USE 学生管理数据库 GO SELE…...

前端开发tips
前端开发tips 关于package.json里面,尖角号(^)和波浪线(~)的区别 在package.json里面,我们可以使用尖角号(^)和波浪线(~)来表示不同的包版本。这些符号通常被…...

实现跨VLAN通信、以及RIP路由协议的配置
一、如下图片: 1. 按照拓扑图所示,将8台计算机分别配置到相应的VLAN中。(20分) 2. 配置实现同一VLAN中的计算机可以通信。(22分) 3. 配置实现PC1,PC2,PC3,PC4可以互相通信,PC5,PC6,PC7,PC8可以互…...

使用python绘制现有彩票记录走势图
在数据分析和可视化的领域中,彩票走势图是一个经典的例子,它可以展示彩票数字随时间的出现频率和趋势。这里使用英国使用EuroMillions彩票的历史数据作为示例,使用Python和Matplotlib库来创建一个简单的走势图。可以在以下网站搜索.csv文件。…...

Kubernetes实战(十)-升级k8s集群
1 Kubernetes(k8s) 集群升级过程 Kubernetes 使用 kubeadm 工具来管理集群组件的升级。在集群节点层面,升级 Kubernetes(k8s)集群的过程可以分为以下几个步骤: 1)检查当前环境和配置是否满足升级要求。 2)升级master主节点&…...

点击el-tree小三角后去除点击后的高亮背景样式,el-tree样式修改
<div class"videoTree" v-loading"loadingTree" element-loading-text"加载中..." element-loading-spinner"el-icon-loading" element-loading-background"rgba(0, 0, 0, 0.8)" > <el-tree :default-expand-all&q…...

【电子取证篇】汽车取证数据提取与汽车取证实例浅析(附标准下载)
【电子取证篇】汽车取证数据提取与汽车取证实例浅析(附标准下载) 关键词:汽车取证,车速鉴定、声像资料鉴定、汽车EDR提取分析 汽车EDR一般记录车辆碰撞前后的数秒(5s左右)相关数据,包括车辆速…...

系列学习前端之第 3 章:一文精通 css
全套学习 HTMLCSSJavaScript 代码和笔记请下载网盘的资料: 链接: 百度网盘 请输入提取码 提取码: 6666 一、CSS基础 1. CSS简介 CSS 的全称为:层叠样式表 ( Cascading Style Sheets ) 。 CSS 也是一种标记语言,用于给 HTML 结构设…...

基于JavaWeb+SSM+Vue马拉松报名系统微信小程序的设计和实现
基于JavaWebSSMVue马拉松报名系统微信小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.…...

leetcode 255.用队列实现栈
255.用队列实现栈 不出意外大概率这几天都会更新 leetcode,如果没有做新的题,大概就会把 leetcode 之前写过的题整理(单链表的题目居多一点)出来写成博客 今天讲的题蛮容易出错的(注意传参啊,最好把队列的…...

排序算法---选择排序
1.实现流程: 1. 把第一个没有排序过的元素设置为最小值; 2. 遍历每个没有排序过的元素; 3. 如果元素 < 现在的最小值; 4. 将此元素设置成为新的最小值; 5. 将最小值和第一个没有排序过的位置交换 选择排序执行流程…...

物联网IC
物联网IC 电子元器件百科 文章目录 物联网IC前言一、物联网IC是什么二、物联网IC的类别三、物联网IC的应用实例四、物联网IC的作用原理总结前言 物联网IC的功能和特性可以根据不同的物联网应用需求来选择和配置,以满足物联网设备在连接、通信、感知和控制方面的需求。 一、物…...

2022年第十一届数学建模国际赛小美赛A题翼龙如何飞行解题全过程文档及程序
2022年第十一届数学建模国际赛小美赛 A题 翼龙如何飞行 原题再现: 翼龙是翼龙目中一个已灭绝的飞行爬行动物分支。它们存在于中生代的大部分时期:从三叠纪晚期到白垩纪末期。翼龙是已知最早进化出动力飞行的脊椎动物。它们的翅膀是由皮肤、肌肉和其他组…...

Blender学习--制作带骨骼动画的机器人
1. 首先创建一个机器人模型 时间关系,这部分步骤有时间补充 2. 然后为机器人创建一副骨架 时间关系,这部分步骤有时间补充 3.骨骼绑定 切换到物体模式,选中机器人头部,Shift选中骨骼,切换到姿态模式,&am…...

单片机学习13——串口通信
单片机的通信功能: 实现单片机和单片机的信息交换,实现单片机和计算机的信息交换。 计算机通信是指计算机与外部设备或计算机与计算机之间的信息交换。 通信有并行通信和串行通信两种方式。 在多微机系统以及现在测控系统中信息的交换多采用串行通信方…...