【细读Spring Boot源码】监听器合集-持续更新中

news2025/1/11 17:00:06

前言

监听器汇总

归属监听器名称作用
cloudBootstrapApplicationListener
cloudLoggingSystemShutdownListener
cloudRestartListener
cloudLoggingSystemShutdownListener
springbootEnvironmentPostProcessorApplicationListener用于触发在spring.factories文件中注册的EnvironmentPostProcessors
BackgroundPreinitializer在后台线程触发一些耗时的早期的初始化,设置 IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME为true来关闭,让类初始化在前台进行
org.springframework.boot.context.configAnsiOutputApplicationListener根据属性spring.output.ansi.enabled的值配置AnsiOutput
org.springframework.boot.context.loggingLoggingApplicationListener它将用于引导日志系统,否则将使用默认配置。

监听器详情

BootstrapApplicationListener

在这里插入图片描述
重新创建了一个SpringApplication上下文,为了添加BootstrapImportSelectorConfiguration配置类,里面会注册所有BootstrapConfiguration类型的配置类。然后进行上下文的run,ConfigFileApplicationListener会去加载bootstrap的配置文件,整合初始化器到新上下文,详细如下分析。

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	// 判断是否开启
	if (!bootstrapEnabled(environment) && !useLegacyProcessing(environment)) {
		// 通过 spring.cloud.bootstrap.enabled 配置属性 true或false
		// 通过 spring.config.use-legacy-processing 配置属性 true或false
		return;
	}
	// 不要在引导程序上下文中侦听事件。保证不会重复执行,如果有bootstrap属性名了就返回
	if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
		return;
	}
	ConfigurableApplicationContext context = null;
	// 拿到 spring.cloud.bootstrap.name 配置属性,配置的名称。没有使用默认的bootstrap
	String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
	// 从父上下文初始化器里获取,一般获取不到
	for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
		if (initializer instanceof ParentContextApplicationContextInitializer) {
			context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
		}
	}
	if (context == null) {
		// 要重新创建一个上下文,为的就是来加载一些配置文件
		context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
		event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
	}

	apply(context, event.getSpringApplication(), environment);
}

bootstrapServiceContext创建新上下文一

  • 构建一个新的环境bootstrapEnvironment 。
  • 给名为bootstrap的环境添加spring.config.name属性、spring.main.web-application-type属性
  • 如果监听器所在上下文环境存在spring.cloud.bootstrap.location就给名为bootstrap的环境添加spring.config.location属性
  • 如果监听器所在上下文环境存在spring.cloud.bootstrap.additional-location就给名为bootstrap的环境添加pring.config.additional-location属性
  • 过滤StubPropertySource类型的环境,把其余环境添加进新环境
// 构建一个新的环境bootstrapEnvironment
ConfigurableEnvironment bootstrapEnvironment = new AbstractEnvironment() {};
MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
String configAdditionalLocation = environment
		.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");

// 添加属性		
Map<String, Object> bootstrapMap = new HashMap<>();
bootstrapMap.put("spring.config.name", configName);
// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
// will fail
// force the environment to use none, because if though it is set below in the
// builder
// the environment overrides it
bootstrapMap.put("spring.main.web-application-type", "none");
if (StringUtils.hasText(configLocation)) {
	bootstrapMap.put("spring.config.location", configLocation);
}
if (StringUtils.hasText(configAdditionalLocation)) {
	bootstrapMap.put("spring.config.additional-location", configAdditionalLocation);
}
bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));

//过滤 StubPropertySource 类型的环境,把其余环境添加进新环境(老的放进新的里)
for (PropertySource<?> source : environment.getPropertySources()) {
	if (source instanceof StubPropertySource) {
		continue;
	}
	bootstrapProperties.addLast(source);
}

bootstrapServiceContext创建新上下文二

使用建造者模式构造一个SpringApplication的builder。并向程序添加BootstrapImportSelectorConfiguration源

// TODO: is it possible or sensible to share a ResourceLoader?
// 设置活动的配置文件
SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles())
		// 设置这个spring应用不打印横幅。设置环境为上面新建环境
		.bannerMode(Mode.OFF).environment(bootstrapEnvironment)
		// Don't use the default properties in this builder
		// 不用关机钩子。不记录启动信息。无web启动
		.registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
// 获取这个新的SpringApplication
final SpringApplication builderApplication = builder.application();
if (builderApplication.getMainApplicationClass() == null) {
	// gh_425:
	// SpringApplication cannot deduce the MainApplicationClass here
	// if it is booted from SpringBootServletInitializer due to the
	// absense of the "main" method in stackTraces.
	// But luckily this method's second parameter "application" here
	// carries the real MainApplicationClass which has been explicitly
	// set by SpringBootServletInitializer itself already.
	// 翻译上面的话:如果由于stackTraces中缺少“main”方法而从SpringBootServletInitializer启动,
	// 则SpringApplication无法在此处推导MainApplicationClass。但幸运的是,
	// 这个方法的第二个参数“application”携带了真正的MainApplicationClass,
	// 它已经由SpringBootServletInitializer自己显式设置了。
	builder.main(application.getMainApplicationClass());
}
if (environment.getPropertySources().contains("refreshArgs")) {
	// If we are doing a context refresh, really we only want to refresh the
	// Environment, and there are some toxic listeners (like the
	// LoggingApplicationListener) that affect global static state, so we need a
	// way to switch those off.
	// 关闭一些监听器
	builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
}
// 向该应用程序添加更多源(配置类和组件):添加 BootstrapImportSelectorConfiguration	
builder.sources(BootstrapImportSelectorConfiguration.class);

bootstrapServiceContext创建新上下文三

最终还是调用SpringApplication的run,但是里面就简单的做了一件事,注册我们的BootstrapImportSelectorConfiguration配置文件

final ConfigurableApplicationContext context = builder.run();
// gh-214 using spring.application.name=bootstrap to set the context id via
// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
// spring.application.name
// during the bootstrap phase.
// 使用spring.application.name=bootstrap通过`ContextIdApplicationContextInitializer`
// 设置上下文id会阻止应用程序在引导阶段获得实际的spring.application.name
context.setId("bootstrap");
// Make the bootstrap context a parent of the app context
// 使引导程序上下文成为应用程序上下文的父上下文
addAncestorInitializer(application, context);
// It only has properties in it now that we don't want in the parent so remove
// it (and it will be added back later)
// 现在它只包含属性,因为我们不想在父级中使用它,所以将其删除(稍后会重新添加)。移除bootstrap属性
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
// 把nocas加载的配置文件merge到应用程序环境里
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;

LoggingSystemShutdownListener

Cleans up the logging system immediately after the bootstrap context is created on startup. Logging will go dark until the ConfigFileApplicationListener fires, but this is the price we pay for that listener being able to adjust the log levels according to what it finds in its own configuration.

在启动时创建bootstrap上下文后立即清理日志系统。在ConfigFileApplicationListener启动之前,日志会一直处于黑暗状态,但这是我们为监听器能够根据它在自己的配置中发现的内容来调整日志级别而付出的代价。

EnvironmentPostProcessorApplicationListener

总分为3个事件ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationFailedEvent

第一阶段ApplicationEnvironmentPreparedEvent

关于应用程序环境准备事件

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	// 获取环境对象
	ConfigurableEnvironment environment = event.getEnvironment();
	// 获取Spring应用容器
	SpringApplication application = event.getSpringApplication();
	// 
	for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
			event.getBootstrapContext())) {
		postProcessor.postProcessEnvironment(environment, application);
	}
}

第二阶段ApplicationPreparedEvent

第三阶段ApplicationFailedEvent

LoggingApplicationListener

public void onApplicationEvent(ApplicationEvent event) {
	if (event instanceof ApplicationStartingEvent) {
		onApplicationStartingEvent((ApplicationStartingEvent) event);
	}
	else if (event instanceof ApplicationEnvironmentPreparedEvent) {
		// 应用程序环境准备事件
		onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
	}
	else if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent((ApplicationPreparedEvent) event);
	}
	else if (event instanceof ContextClosedEvent) {
		onContextClosedEvent((ContextClosedEvent) event);
	}
	else if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent();
	}
}

AnsiOutputApplicationListener

BackgroundPreinitializer

后台初始化有5项:

  • ConversionServiceInitializer:Spring的ConversionService的早期初始值设定项
  • ValidationInitializer:javax.validation的早期初始值设定项
  • MessageConverterInitializer:Spring MessageConverters的早期初始值设定项。
  • JacksonInitializer:Jackson的早期初始化程序
  • CharsetInitializer:字符初始化
    在这里插入图片描述

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

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

相关文章

osg操控器之动画路径操控器osgGA::AnimationPathManipulator分析

目录 1. 前言 2. 示例代码 3. 动画路径操控器源码分析 3.1. 构造函数 3.2. home函数 3.3. handle函数 3.3.1 帧事件处理 3.3.2. 按键事件处理 4. 主要接口说明 1. 前言 osg官方提供了很多操控器&#xff0c;在源码目录下的src\osgGA目录下&#xff0c;cpp文件名含有Ma…

初识AUTOSAR

目录 应用层 Runnable Port 运行时环境 基础软件层 总结 AUTOSAR&#xff0c;全称为Automotive Open System Architecture&#xff0c;即汽车开放系统架构。它最初于2003年由当时全球各家顶级汽车制造商&#xff08;奔驰、宝马、大众等&#xff09;、零部件供应商&#x…

【Unity入门】21.预制体

【Unity入门】预制体 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;预制体制作 &#xff08;1&#xff09;什么是预制体 这一章节的博客&#xff0c;我们将会学习一个预制体的概念。什么是…

【C语言进阶】-- 重点字符串函数内存函数及其模拟实现(strlen,strcmp,strcat...memcpy,memmove)

目录 1、strlen 1.1 strlen的模拟实现 2、strcpy 2.1 strcpy的模拟实现 3、strcat 3.1 strcat的模拟实现 4、strcmp 4.1 strcmp的模拟实现 5、strstr 5.1 strstr的模拟实现 6、memcpy 6.1 memcpy的模拟实现 7、memmove 7.1 memmove的模拟实现 前言 C语言中对字符…

Ant Design Vue,a-table组件加序号

<a-table:columns"columns":pagination"pagination":data-source"dataSource":defaultExpandAllRows"true"change"tableChange":rowKey"(record, index) > index 1"> columns是表格列的配置&#xff0c…

【2023五一杯数学建模】 B题 快递需求分析问题 建模方案及MATLAB实现代码

【2023五一杯数学建模】 B题 快递需求分析问题 1 题目 请依据以下提供的附件数据和背景内容&#xff0c;建立数学模型&#xff0c;完成接下来的问题&#xff1a;问题背景是&#xff0c;网络购物作为一种重要的消费方式&#xff0c;带动着快递服务需求飞速增长&#xff0c;为我…

25特别放送:我的Gopher成长之路

很早就开始准备这篇文章了,但总是想了又想不知怎样才能更好的写下自己最真实的想法,后来在经过了好几个晚上睡前的思考后才得以完成。 首先,写这篇文章的目的并不是为了吹嘘Go语言有多厉害,也不是鼓励大家都来学习Go语言,仅是为了记录和分享。当然如果是兴趣使然,那么欢…

实时更新天气微信小程序开发

1.新建一个天气weather项目 2.在app.json中创建一个路由页面 当我们点击保存的时候&#xff0c;微信小程序会自动的帮我们创建好页面 3.在weather页面上书写我们的骨架 4.此时我们的页面很怪&#xff0c;因为没有给它添加样式和值。此时我们给它一个样式。&#xff08;样式写在…

蓝桥杯——二分专题

二分分为&#xff1a;实数二分&#xff0c;二分理论题 二分套路题&#xff1a;最小值最大化&#xff0c;最大值最小化 运用二分满足条件&#xff1a;有界&#xff0c;单调。 1.两个二分模板 找>x的第一个&#xff0c;mid&#xff08;lowhigh&#xff09;//2 &#xff0c;没…

java基础知识——23.正则表达式

这篇文章我们简略的讲一下java的正则表达式 目录 1.正则表达式概述 2.正则表达式的简单匹配规则 3.正则表达式的复杂匹配规则 4.正则表达式的分组匹配规则 5.正则表达式的非贪婪匹配 6.使用正则表达式进行搜索和替换 1.正则表达式概述 首先&#xff0c;我们需要明确一个…

leetcode 面试题 02.04. 分割链表

原题为&#xff1a; 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在大于或等于x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 测试示例如下&#xff1a; 输入&#xff1a;head [1,4…

Flink第一章:环境搭建

系列文章目录 Flink第一章:环境搭建 文章目录 系列文章目录前言一、Idea项目1.创建项目2.pom.依赖3.DataSet4.DataStreaming 二、环境搭建1.Standalone2.Flink on Yarn 总结 前言 Flink也是现在现在大数据技术中火爆的一门,反正大数据的热门技术学的也差不多了,啃完Flink基本…

Packet Tracer - 研究直连路由

Packet Tracer - 研究直连路由 目标 第 1 部分&#xff1a;研究 IPv4 直连路由 第 2 部分&#xff1a;研究 IPv6 直连路由 拓扑图 背景信息 本活动中的网络已配置。 您将登录路由器并使用 show 命令发现并回答以下有关直连路由的问题。 注&#xff1a;用户 EXEC 密码是 c…

A2B汽车音响系统开发设计与改装

hezkz17进数字音频系统研究开发答疑群 1 前装与后装

安装了Volar插件vue文件没有显示Volar的图标

vue3官网 推荐使用Volar来替换Vetur 一、安装Volar 安装Volar前&#xff1a; 安装Volar后&#xff1a; 二、安装Volar插件后&#xff0c;无法显示高亮 之前我安装Volar插件后&#xff0c;vue文件的<script>、<template>、<style>标签仍然是白色的&#xff0c…

Doris(17):动态分区

动态分区是在 Doris 0.12 版本中引入的新功能。旨在对表级别的分区实现生命周期管理(TTL)&#xff0c;减少用户的使用负担。 目前实现了动态添加分区及动态删除分区的功能。 1 原理 在某些使用场景下&#xff0c;用户会将表按照天进行分区划分&#xff0c;每天定时执行例行任…

【网课平台】Day14.集成RabbitMQ:消息队列实现异步通知

文章目录 一、需求&#xff1a;支付通知1、需求分析2、技术方案3、集成RabbitMQ4、生产端发送消息5、消费方发送消息 二、需求&#xff1a;在线学习1、需求分析2、表设计与实体类3、接口定义--查询课程4、接口定义获取视频5、Service层开发6、FeignClient定义7、代码完善 三、需…

数字设计小思 - D触发器与死缠烂打的亚稳态

前言 本系列整理数字系统设计的相关知识体系架构&#xff0c;为了方便后续自己查阅与求职准备。在FPGA和ASIC设计中&#xff0c;D触发器是最常用的器件&#xff0c;也可以说是时序逻辑的核心&#xff0c;本文根据个人的思考历程结合相关书籍内容和网上文章&#xff0c;聊一聊D…

Hudi数据湖技术之数据中心案例实战

目录 1 案例架构2 业务数据2.1 客户信息表2.2 客户意向表2.3 客户线索表2.4 线索申诉表2.5 客户访问咨询记录表 3 Flink CDC 实时数据采集3.1 开启MySQL binlog3.2 环境准备3.3 实时采集数据3.3.1 客户信息表3.3.2 客户意向表3.3.3 客户线索表3.3.4 客户申诉表3.3.5 客户访问咨…

微信小程序 WebSocket 通信 —— 在线聊天

在Node栏目就讲到了Socket通信的内容&#xff0c;使用Node实现Socke通信&#xff0c;还使用两个流行的WebSocket 库&#xff0c;ws 和 socket.io&#xff0c;在小程序中的WebSocket接口和HTML5的WebSocket基本相同&#xff0c;可以实现浏览器与服务器之间的全双工通信。那么本篇…