当前位置: 首页 > news >正文

【Spring Boot 源码学习】ApplicationListener 详解

Spring Boot 源码学习系列

在这里插入图片描述

ApplicationListener 详解

  • 引言
  • 往期内容
  • 主要内容
    • 1. 初识 ApplicationListener
    • 2. 加载 ApplicationListener
    • 3. 响应应用程序事件
  • 总结

引言

书接前文《初识 SpringApplication》,我们从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 实例化过程。其中,《BootstrapRegistryInitializer 详解》 和 《ApplicationContextInitializer 详解》博文中,Huazie 已经带大家详细分析了 BootstrapRegistryInitializerApplicationContextInitializer 的加载和初始化过程,如下还有 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 注解修饰。

知识点: @FunctionalInterfaceJava 8 中引入的一个注解,用于标识一个函数式接口。函数式接口是只有一个抽象方法的接口,常用于实现 Lambda 表达式和方法引用。
使用 @FunctionalInterface 注解可以向编译器指示该接口是一个函数式接口,从而在编译时进行类型检查,确保该接口 只包含一个抽象方法。此外,该注解还可以为函数式接口生成特殊的方法,如默认方法(default method)和 静态方法(static method),这些方法可以在接口中提供更多的功能,这里就不赘述了,感兴趣的朋友可以自行查阅相关函数式接口的资料。

ApplicationListenerSpring 中应用程序事件监听器实现的接口。它基于观察者设计模式的java.util.EventListener 接口的标准。在注册到 Spring ApplicationContext 时,事件将进行相应的过滤,只有匹配的事件对象才会使该监听器被调用。

ApplicationListener 接口中,我们可以看到它定义了一个 onApplicationEvent(E event) 方法,当监听事件被触发时,onApplicationEvent 方法就会被调用执行。onApplicationEvent 方法一般用于处理应用程序事件,参数 eventApplicationEvent 的子类,也就是具体要响应处理的各种类型的应用程序事件。例如,当某个特定事件发生时,你可能想要记录日志、更新数据库、发送电子邮件等等。

另外,ApplicationListener 接口还提供了一个静态方法 forPayload(Consumer<T> consumer),用于创建一个新的 ApplicationListener 实例。这个方法接受一个 Consumer<T> 类型的参数,这个参数是一个函数接口,它接受一个泛型参数 T,并对其执行一些操作。通过这个方法,你可以将一个 Consumer 函数作为参数,然后返回一个对应的事件监听器。这个监听器会在事件发生时,调用 Consumer 函数处理事件的有效载荷【即事件中包含的有效信息或数据】。

2. 加载 ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

上述代码是 SpringApplication 的核心构造方法中的逻辑,它用于加载实现了 ApplicationListener 接口的监听器实例集合,并将该监听器实例集合设置到 SpringApplicationlisteners 变量中。

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. 响应应用程序事件

这里我们需要查看 SpringApplicationrun(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 刷新并启动之后,CommandLineRunnerApplicationRunner 未被调用之前,该方法被调用
  • 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》&#xff0c;我们从 Spring Boot 的启动类 SpringApplication 上入手&am…...

HCIP---RSTP/MSTP

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

探索开源游戏的乐趣与无限可能 | 开源专题 No.47

CleverRaven/Cataclysm-DDA Stars: 9.0k License: NOASSERTION Cataclysm&#xff1a;Dark Days Ahead 是一个回合制的生存游戏&#xff0c;设定在一个后启示录世界中。尽管有些人将其描述为 “僵尸游戏”&#xff0c;但 Cataclysm 远不止于此。在这个残酷、持久、程序生成的世…...

springboot_3.2_freemark_基础环境配置

springboot_3.2_freemark_基础环境配置 一、前言二、环境三、相关资料四、目标五、默认配置项六、构建springboot 3.2项目6.1 pom.xml 内容&#xff1a;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 源&#xff0c;对比前后yum源5.安装mysql服务6.查看配置文件和数据存储位置7.启动服务和查看启动服务8.登录9.配置my.cnf 1.卸载不要的环境 先检查是否有mariadb存在 ps ajx |grep mariadb如果…...

有病但合理的 ChatGPT 提示语

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

this.$emit(‘update:isVisible‘, false)作用

这个写是不是很新颖&#xff0c;传父组件传值&#xff01;这是什么鬼。。。 假设你有以下逻辑业务。在A页面弹出一个组件B&#xff0c;A组件里面使用B组件&#xff0c;是否展示B组件你使用的是baselineShow变量控制&#xff01; <BaselineData :isVisible.sync"basel…...

CnetSDK .NET OCR Library SDK Crack

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

基于Solr的全文检索系统的实现与应用

文章目录 一、概念1、什么是Solr2、与Lucene的比较区别1&#xff09;Lucene2&#xff09;Solr 二、Solr的安装与配置1、Solr的下载2、Solr的文件夹结构3、运行环境4、Solr整合tomcat1&#xff09;Solr Home与SolrCore2&#xff09;整合步骤 5、Solr管理后台1&#xff09;Dashbo…...

【rabbitMQ】rabbitMQ控制台模拟收发消息

目录 1.新建队列 2.交换机绑定队列 3.查看消息是否到达队列 总结&#xff1a; 1.新建队列 2.交换机绑定队列 点击amq.fonout 3.查看消息是否到达队列 总结&#xff1a; 生产者&#xff08;publisher&#xff09;发送消息&#xff0c;先到达交换机&#xff0c;再到队列&…...

Java NIO, IO 整理

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

【数据结构】——排序篇(下)

前言&#xff1a;前面我们的排序已经详细的讲解了一系列的方法&#xff0c;那么我们现在久之后一个归并排序了&#xff0c;所以我们现在就来讲解一下归并排序。 归并排序&#xff1a; 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法…...

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主要⽤于⼤数据的并⾏计算&#xff0c;⽽Hadoop在企业主要⽤于⼤数据的存储&#xff08;⽐如HDFS、Hive和HBase 等&#xff09;&#xff0c;以及资源调度&#xff08;Yarn&#xff09;。但是也有很多公司也在使⽤MR2进…...

面试经典150题(10-13)

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

Sql server数据库数据查询

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

前端开发tips

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

实现跨VLAN通信、以及RIP路由协议的配置

一、如下图片&#xff1a; 1. 按照拓扑图所示&#xff0c;将8台计算机分别配置到相应的VLAN中。&#xff08;20分&#xff09; 2. 配置实现同一VLAN中的计算机可以通信。&#xff08;22分&#xff09; 3. 配置实现PC1,PC2,PC3,PC4可以互相通信&#xff0c;PC5,PC6,PC7,PC8可以互…...

使用python绘制现有彩票记录走势图

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

Kubernetes实战(十)-升级k8s集群

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

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...