SpringBoot 源码分析(三) 监听器分析以及属性文件加载分析
前言
在创建SpringBoot项目的时候会在对应的application.properties或者application.yml文件中添加对应的属性信息,这些属性文件是什么时候被加载的?如果要实现自定义的属性文件怎么来实现?在讲属性加载之前先讲下监听器分析。

一、监听器分析
1、SpringBoot源码之监听器设计
1.1 观察者模式
监听器的设计会使用到Java设计模式中的观察者模式。
观察者模式又称为发布/订阅(Publish/Subscribe)模式,在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
在java.util包中包含有基本的Observer接口和Observable抽象类.功能上和Subject接口和Observer接口类似.不过在使用上,就方便多了,因为许多功能比如说注册,删除,通知观察者的那些功能已经内置好了.
1.2 SpringBoot中监听器的设计
然后我们来看下SpringBoot启动这涉及到的监听器这块是如何实现的。
2.1 初始化操作
在SpringApplication的构造方法中会加载所有声明在spring.factories中的监听器。
在springboot的监听器有如下两类:
# Run Listeners
#事件发布运行监听器,是springboot中配置的唯一一个应用运行监听器,
作用是通过一个多路广播器,将springboot运行状态的变化,构建成事件,并广播给各个监听器
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener(),\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
通过对这些内置监听器的源码查看我们发现这些监听器都实现了 ApplicationEvent接口。也就是都会监听 ApplicationEvent发布的相关的事件。ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。

2.2 run方法
在SpringApplication.run()方法中是如何发布对应的事件的?
首先会通过getRunListeners方法来获取我们在spring.factories中定义的SpringApplicationRunListener类型的实例。也就是EventPublishingRunListener。



加载这个类型的时候会同步的完成实例化。

实例化操作就会执行EventPublishingRunListener.

在这个构造方法中会绑定我们前面加载的11个过滤器。

到这其实我们就已经清楚了EventPublishingRunListener和我们前面加载的11个监听器的关系了。然后在看事件发布的方法。

查看starting()方法。


进入到multicastEvent中方法中我们可以看到具体的触发逻辑

以ConfigFileApplicationListener为例。

触发会进入ConfigFileApplicationListener对象的onApplicationEvent方法中

通过代码我们可以发现当前的事件是ApplicationStartingEvent事件,都不满足,所以ConfigFileApplicationListener在SpringBoot项目开始启动的时候就不会做任何的操作。而当我们在配置环境信息的时候,会发布对应的事件来触发


继续进入

继续进入

然后再触发ConfigFileApplicationListener监听器的时候就会触发如下方法了

其实到这儿,后面的事件发布与监听器的处理逻辑就差不多是一致了。到这儿对应SpringBoot中的监听器这块就分析的差不错了。像SpringBoot的属性文件中的信息什么时候加载的就是在这些内置的监听器中完成的。

官方内置的事件有:

二、属性加载过程分析
1. 找到入口
在SpringApplication.run()方法,在该方法中会针对SpringBoot项目启动的不同的阶段来发布对应的事件。

处理属性文件加载解析的监听器是 ConfigFileApplicationListener ,这个监听器监听的事件有两个。

进入SpringApplication.prepareEnvironment()方法中发布的事件其实就是ApplicationEnvironmentPreparedEvent事件。进入代码查看。

进行进入

继续进入会看到对应的发布事件:ApplicationEnvironmentPreparedEvent

结合上篇文件的内容,我们知道在initialMulticaster中是有ConfigFileApplicationListener这个监听器的。

2. ConfigFileApplicationListener
2.1 主要流程分析
ConfigFileApplicationListener中具体的如何来处理配置文件的加载解析的。

根据逻辑我们直接进入onApplicationEnvironmentPreparedEvent()方法中。

系统提供那4个不是重点,重点是 ConfigFileApplicationListener 中的这个方法处理.

直接进入ConfigFileApplicationListener.postProcessEnvironment()方法。

在进入addPropertySources()方法中会完成两个核心操作,
1。创建Loader对象,2。调用Loader对象的load方法,

2.2 Loader构造器
现在我们来看下在Loader构造器中执行了什么操作。

通过源码我们可以发现在其中获取到了属性文件的加载器、从spring.factories文件中获取,对应的类型是 PropertySourceLoader类型。

而且在loadFactories方法中会完成对象的实例化。

到这Loader的构造方法执行完成了,然后来看下load()方法的执行。先把代码贴上
void load() {FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,(defaultProperties) -> {// 创建默认的profile 链表this.profiles = new LinkedList<>();// 创建已经处理过的profile 类别this.processedProfiles = new LinkedList<>();// 默认设置为未激活this.activatedProfiles = false;// 创建loaded对象this.loaded = new LinkedHashMap<>();// 加载配置 profile 的信息,默认为 defaultinitializeProfiles();// 遍历 Profiles,并加载解析while (!this.profiles.isEmpty()) {// 从双向链表中获取一个profile对象Profile profile = this.profiles.poll();// 非默认的就加入,进去看源码即可清楚if (isDefaultProfile(profile)) {addProfileToEnvironment(profile.getName());}load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));this.processedProfiles.add(profile);}// 解析 profileload(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));// 加载默认的属性文件 application.propertiesaddLoadedPropertySources();applyActiveProfiles(defaultProperties);});}
然后我们进入具体的apply()方法中来查看。

中间的代码都有注释,主要是处理profile的内容。

首先是getSearchLocations()方法,在该方法中会查询默认的会存放对应的配置文件的位置,如果没有自定义的话,路径就是 file:./config/ file:./ classpath:/config/ classpath:/ 这4个


然后回到load方法中,遍历4个路径,然后加载对应的属性文件。

getSearchNames()获取的是属性文件的名称。如果自定义了就加载自定义的

否则加载默认的application文件。

再回到前面的方法

进入load方法,会通过前面的两个加载器来分别加载application.properties和application.yml的文件。

loader.getFileExtensions()获取对应的加载的文件的后缀。



进入loadForFileExtension()方法,对profile和普通配置分别加载

继续进入load方法





开始加载我们存在的application.properties文件。
2.3 properties加载
在找到了要加载的文件的名称和路径后,我们来看下资源加载器是如何来加载具体的文件信息的。

进入loadDocuments方法中,我们会发现会先从缓存中查找,如果缓存中没有则会通过对应的资源加载器来加载了。

此处是PropertiesPropertySourceLoader来加载的。


进入loadProperties方法

之后进入load()方法看到的就是具体的加载解析properties文件中的内容了。感兴趣的可以看下具体的逻辑,本文就给大家介绍到这里了。

相关文章:
SpringBoot 源码分析(三) 监听器分析以及属性文件加载分析
前言 在创建SpringBoot项目的时候会在对应的application.properties或者application.yml文件中添加对应的属性信息,这些属性文件是什么时候被加载的?如果要实现自定义的属性文件怎么来实现?在讲属性加载之前先讲下监听器分析。 一、监听器分…...
记录nvm use node.js版本失败,出现报错: exit status 1: ��û���㹻��Ȩ��ִ�д˲�����
使用管理员权限运行cmd,再使用nvm use node.js版本号 参考: nvm use (node版本号)时报错: exit status 1: �����㹻��Ȩ��ִ…...
【蓝牙协议】简介:蓝牙芯片、蓝牙协议架构
文章目录 蓝牙芯片架构另一个视角由下到上看:Controller-->Host由上到下看:Host-->Controller 蓝牙协议架构视角HW层——蓝牙芯片层Transport——数据传输层HOST——协议层 总结 参考:https://zhuanlan.zhihu.com/p/585248998 参考&…...
【深度学习】
什么是深度学习? 感知器 为了实现模拟人类的学习,科学家们首先设计了构成神经网络的基本结构神经元(感知器模型),然后再由大量的神经元构成复杂的,能够实现各种功能的神经网络。 这种模式和超能陆战队中的…...
centos启动tomcat 并指定jdk 版本
在tomcat的catalina.sh文件手动设置JAVA_HOME变量即可 例如: 前提是文件存在 保存配置重新启动tomcat...
day37(事件轮询机制 ajaxGet执行步骤与案例(五个步骤) ajax属性 PHP返回JSON对象(两种))
一.事件轮询机制 1. 无论同步还是异步代码都要经过主线程编译,同步代码开始排在执行栈(主线程)上,异步代码开 始存放在任务队列中 2. 主线程优先执行同步代码,同步代码必须前一行执行完,后一行才能执行;当异步代码…...
Flume基本使用--mysql数据输出
MySQL数据输出 在MySQL中建立数据库school,在数据库中建立表student。SQL语句如下: create database school; use school; create table student(id int not null,name varchar(40),age int,grade int,primary key(id) ); 请使用Flume实时捕…...
MySQL——EXPLAIN用法详解
EXPLAIN是MySQL官方提供的sql分析的工具之一,可以用于模拟优化器执行sql查询语句,从而知道MySQL是如何处理sql语句。EXPLAIN主要用于分析查询语句或表结构的性能瓶颈。 以下是基于MySQL5.7.19版本进行分析的,不同版本之间略有差异。 1、EXP…...
69 划分字母区间
划分字母区间 题解1 贪心1(方法略笨,性能很差)题解2 贪心2(参考标答) 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 注意,划分结果需要满足&am…...
文件上传漏洞(1), 文件上传绕过原理
文件上传漏洞 一, 前端校验上传文件 添加 Javascript 代码,然后在 form 表单中 添加 onsubmit"returb checkFile()" <script>function checkFile() {// var file document.getElementsByName(photo)[0].value;var file document.getElementByI…...
【ARM 嵌入式 C 入门及渐进 10 -- 冒泡排序 选择排序 插入排序 快速排序 归并排序 堆排序 比较介绍】
文章目录 排序算法小结排序算法C实现 排序算法小结 C语言中常用的排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序。下面我们来一一介绍: 冒泡排序(Bubble Sort):冒泡排序是通过比较相邻元素的大小进行排…...
虹科 | 解决方案 | 汽车示波器 学校教学方案
虹科Pico汽车示波器是基于PC的设备,特别适用于大课堂的教学、备课以及与师生的互动交流。老师展现讲解波形数据,让学生直观形象地理解汽车的工作原理 高效备课 课前实测,采集波形数据,轻松截图与标注,制作优美的课件&…...
广播和组播(多播)
广播 概述 广播(broadcast)是指封包在计算机网络中传输时,目的地址为网络中所有设备的一种传输方式。实际上,这里所说的“所有设备”也是限定在一个范围之中,称为“广播域”。并非所有的计算机网络都支持广播…...
【Linux】gdb调试
目录 进入调试查看代码运行代码断点打断点查断点删断点从一个断点转跳至下一个断点保留断点但不会运行该断点 退出调试逐过程逐语句监视跳转至指定行运行结束当前函数 进入调试 指令:gdb 【可执行文件】: 查看代码 :l 【第几行】如果输入指…...
MySQL创建函数及其使用
MySQL创建函数及其使用 一、MySQL 创建函数二、示例 一、MySQL 创建函数 MySQL 函数是一种可重用的代码块,可以接受输入参数并返回值。你可以在 MySQL 中创建各种类型的函数,包括系统函数、用户定义函数和存储过程。在此处,我们将重点关注用…...
大数据-Storm流式框架(四)---storm容错机制
1、集群节点宕机 Nimbus服务器 硬件 单点故障?可以搭建HA jStorm搭建 nimbus的HA nimbus的信息存储到zookeeper中,只要下游没问题(进程退出)nimbus退出就不会有问题, 如果在nimbus宕机,也不能提交…...
SpringBoot项目把Mysql从5.7升级到8.0
首先你需要把之前的库导入到mysql库导入到8.0的新库中。(导入的时候会报错我是通过navcat备份恢复的) 1、项目中需要修改pom文件的依赖 mysql 和 jdbc <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java&…...
RK3568-适配at24c04模块
将at24c04模块连接到开发板i2c2总线上 i2ctool查看i2c2总线上都有哪些设备 UU表示设备地址的从设备被驱动占用,卸载对应的驱动后,UU就会变成从设备地址。at24c04模块设备地址 0x50和0x51是at24c04模块i2c芯片的设备地址。这个从芯片手册上也可以得知。A0 A1 A2表示的是模块对…...
Banana Pi BPI-W3 ArmSoM-W3之RK3588-MIPI-DSI屏幕调试笔记
一. 简介 本文是基于RK3588平台,MIPI屏调试总结。 二. 环境介绍 硬件环境: ArmSoM-W3 RK3588开发板、MIPI-DSI显示屏( ArmSoM官方配件 )软件版本: OS:ArmSoM-W3 Debian11 三. MIPI屏幕调试 3.1 调试总览,调试步骤分…...
Git的远程仓库
Git的远程仓库 添加远程仓库从远程库克隆 添加远程仓库 你在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
