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

前言

监听器汇总

归属监听器名称作用
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:字符初始化
    在这里插入图片描述

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

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

相关文章

市级大数据中心大数据资源平台概要设计方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 大数据管理中心发展背景 为建设卓越全球城市&#xff0c;实现政府治理能力现代化目标&#xff0c;由市大数据中心牵头&#xff0c;在政务公共数据管理和互联网政务服务方面…

numpy的下载、数据类型、属性、数组创建

下载numpy 因为numpy不依赖于任何一个包所以numpy可以直接使用pip命令直接下载 下载命令&#xff1a; pip install numpy # 默认从https://pypi.org/simple 下载 pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple/ # 从清华大学资源站点下载 pip install nump…

UG NX二次开发(C#)-显示-更改对象颜色

文章目录 1、前言2、UG NX中的更换对象颜色的功能3、采用UG NX二次开发实现颜色修改3.1 采用直接赋值对象颜色不能直接更改对象颜色3.2 采用NewDisplayModification的方法如下:1、前言 当一个三维模型展现在我们面前时,总会有颜色赋予三维模型的对象上,比如红色、蓝色、银灰…

if条件语句

if条件语句 条件测试 test 测试表达式是否成立&#xff0c;若成立返回0&#xff0c;否则返回其他数值 格式1 &#xff1a;test 条件表达式&#xff1b;格式2 &#xff1a;[ 条件表达式 ] echo $?参数作用-d测试是否为目录 (Directory)-e测试目录或文件是否存在(Exist)-f测…

直线导轨水平仪零位调整方法

对于直线导轨的使用&#xff0c;相信很多人都知道&#xff0c;这主要是因为直线导轨的使用范围非常广泛&#xff0c;小到抽屉&#xff0c;大到机械设备&#xff0c;我们都能看到他的身影&#xff0c;接触得多自然就熟悉了。 事实上&#xff0c;大家对直线导轨的了解可能就仅限于…

Cortex-A7中断详解(一)

STM32中断系统回顾 中断向量表NVIC&#xff08;内嵌向量中断控制器&#xff09;中断使能中断服务函数 中断向量表 中断向量表是一个表&#xff0c;表里面存放的是中断向量。 中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量&#xff0c;因此中断向量表是一系…

【Linux入门】linux指令(1)

【Linux入门】linux指令&#xff08;1&#xff09; 目录 【Linux入门】linux指令&#xff08;1&#xff09;操作系统登录服务器Linux下的基本指令ls指令pwd指令Linux路径分割符 /cd指令touch指令mkdir指令&#xff08;重要&#xff09;rmdir指令&&rm指令&#xff08;重…

linux实现网络程序

1️⃣ 在linux下&#xff0c;通过套接字实现服务器和客户端的通信。 2️⃣ 实现单线程、多线程通信。或者实现线程池来通信。 3️⃣ 优化通信&#xff0c;增加守护进程。 有情提醒&#xff0c;类里面默认的函数是内联。内联函数在调用的地方展开&#xff0c;没有函数地址&…

Mac使用命令行工具解压和压缩rar文件

目前在Mac电脑里支持解压缩的格式主要有&#xff1a;zip、gz等&#xff0c;但是还不支持rar格式的文件&#xff0c;接下来带着大家学习一下如何解压缩rar格式文件。 1.下载rar工具 打开&#xff1a;https://www.rarlab.com/download.htm 根据自己电脑的芯片要求选择自己的安装…

【计算机基本原理-数据结构】数据结构中树的详解

【计算机基本原理-数据结构】数据结构中树的详解 1&#xff09;总览2&#xff09;树的相关概念3&#xff09;二叉树、满二叉树、完全二叉树4&#xff09;二叉查找树 - BST5&#xff09;平衡二叉树 - AVL6&#xff09;红黑树7&#xff09;哈弗曼树8&#xff09;B 树9&#xff09…

TCP流量控制与拥塞控制

什么是流量控制 一条TCP连接的每一侧主机都为该连接设置了接收缓存。当该TCP连接接收到正确的、有序的报文段&#xff0c;就会将数据放入接收缓存。相关联的应用会从缓存中读取数据。 如果发送者发送数据过快、过多&#xff0c;而接收方的应用程序从缓冲区读取的速度较慢&…

机器学习实战教程(十):逻辑回归

概述 逻辑回归&#xff08;Logistic Regression&#xff09;是一种用于解决二分类或多分类问题的统计学习方法。它以自变量线性组合的形式进行建模&#xff0c;并使用Sigmoid函数将结果映射到[0, 1]的值域内&#xff0c;表示样本属于某个类别的概率。 Logistic Regression是最…

Stable Diffusion-生式AI的新范式

! 扩散模型&#xff08;Stable Diffusion)现在是生成图像的首选模型。由于扩散模型允许我们以提示( prompts)为条件生成图像&#xff0c;我们可以生成我们所选择的图像。在这些文本条件的扩散模型中&#xff0c;稳定扩散模型由于其开源性而最为著名。 在这篇文章中&#xff0…

STM32平衡小车 TB6612电机驱动学习

TB6612FNG简介 单片机引脚的电流一般只有几十个毫安&#xff0c;无法驱动电机&#xff0c;因此一般是通过单片机控制电机驱动芯片进而控制电机。TB6612是比较常用的电机驱动芯片之一。 TB6612FNG可以同时控制两个电机&#xff0c;工作电流1.2A&#xff0c;最大电流3.2A。 VM电…

力劲塑机:用CRM“塑造”数字化能力

你知道吗&#xff1f;从手机到电脑&#xff0c;从暖气到扶梯&#xff0c;从家用电器到汽车、摩托车&#xff0c;从眼镜、手表到拉链、纽扣&#xff0c;这些物品的生产过程都离不开压铸和注塑工艺。如果说压铸和注塑这个几百亿的产业带动了几万亿的市场&#xff0c;一点也不夸张…

fc坦克大战游戏完美复刻

文章目录 一、 介绍二、 制作基本物体三、 控制玩家坦克移动、转向四、 子弹脚本、爆炸脚本五、 敌人AI寻路算法六、 坦克生成点脚本七、 用链表实例化地图八、 玩家游戏控制器脚本九、 添加音效十、 资源包 一、 介绍 儿时经典游戏《坦克大战》完整复刻 发射子弹、生成敌人、…

巧用千寻位置GNSS软件|一文教会横断面测量

测横断面主要用于线路工程和水利工程的前期设计中&#xff0c;在线路平曲线设计好之后&#xff0c;千寻位置GNSS软件可用于在中桩处测定垂直于线路中线方向原地貌的地面起伏的数据&#xff0c;本期就为大家介绍具体的操作技巧。 点击【测量】->【测横断面】&#xff0c;选择…

java——最小的K个数

题目链接 牛客在线oj题——最小的K个数 题目描述 给定一个长度为 n 的可能有重复值的数组&#xff0c;找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字&#xff0c;则最小的4个数字是1,2,3,4(任意顺序皆可)。 数据范围&#xff1a;0≤k,n≤10000&…

Flink之TaskManager内存解析

一、CK失败 Flink任务的checkpoint操作失败大致分为两种情况&#xff0c;ck decline和ck expire: &#xff08;1&#xff09;ck decline 发生ck decline情况时&#xff0c;我们可以通过查看JobManager.log或TaskManager.log查明具体原因。其中有一种特殊情况为ck cancel&…

idea使用 ( 二 ) 创建java项目

3.创建java项目 3.1.创建普通java项目 3.1.1.打开创建向导 接 2.3.1.创建新的项目 也可以 从菜单选择建立项目 会打开下面的选择界面 3.1.2.不使用模板 3.1.3.设置项目名 Project name : 项目名 Project location : 项目存放的位置 确认创建 3.1.4.关闭tips 将 Dont s…