【Spring Boot 源码学习】深入应用上下文初始化器实现

《Spring Boot 源码学习系列》

在这里插入图片描述

深入应用上下文初始化器实现

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • 3.1 spring-boot 子模块中内置的实现类
      • 3.1.1 ConfigurationWarningsApplicationContextInitializer
      • 3.1.2 ContextIdApplicationContextInitializer
      • 3.1.3 DelegatingApplicationContextInitializer
      • 3.1.4 RSocketPortInfoApplicationContextInitializer
      • 3.1.5 ServerPortInfoApplicationContextInitializer
    • 3.2 spring-boot-autoconfigure 子模块中内置的实现类
      • 3.2.1 SharedMetadataReaderFactoryContextInitializer
      • 3.2.2 ConditionEvaluationReportLoggingListener
    • 3.3 自定义应用上下文初始化器实现
      • 3.3.1 定义 DemoApplicationContextInitializer
      • 3.3.2 添加 DemoApplicationContextInitializer
      • 3.3.3 实际演示
  • 四、总结

一、引言

前面的博文《ApplicationContextInitializer 详解》,Huazie 带大家详细分析了 ApplicationContextInitializer 的加载和初始化的逻辑,不过有关 ApplicationContextInitializer 接口的实现尚未提及 。那本篇 Huazie 就带大家一起分析 Spring Boot 中预置的应用上下文初始化器实现【即 ApplicationContextInitializer 接口实现类】的源码,了解在 Spring 容器刷新之前初始化应用程序上下文的一些具体操作。

在这里插入图片描述

二、往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

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 源码学习】ApplicationListener 详解
【Spring Boot 源码学习】SpringApplication 的定制化介绍
【Spring Boot 源码学习】BootstrapRegistry 详解
【Spring Boot 源码学习】深入 BootstrapContext 及其默认实现
【Spring Boot 源码学习】BootstrapRegistry 初始化器实现
【Spring Boot 源码学习】BootstrapContext的实际使用场景

三、主要内容

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

3.1 spring-boot 子模块中内置的实现类

我们先来看一张截图:

在这里插入图片描述

从上图中可以看出,spring-boot 子模块中配置的 ApplicationContextInitializer 实现一共有 5 个,下面我们一一来介绍下:

3.1.1 ConfigurationWarningsApplicationContextInitializer

该类用于报告常见配置错误的警告,我们来看看相关源码:

public class ConfigurationWarningsApplicationContextInitializerimplements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext context) {context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));}// 省略其他。。。
}

阅读上述源码,我们可以看到 initialize 方法里,通过 ConfigurableApplicationContextaddBeanFactoryPostProcessor 方法,在 应用程序上下文 中添加了一个 BeanFactoryPostProcessor 实现【 该实现类为 ConfigurationWarningsPostProcessor】。

BeanFactoryPostProcessorSpring 框架中的一个接口,它的作用是在 Spring 容器初始化时对 Bean 的定义进行修改或增强【添加属性、设置依赖关系等】。

在介绍 ConfigurationWarningsPostProcessor 之前,先来看看 getChecks 方法:

protected Check[] getChecks() {return new Check[] { new ComponentScanPackageCheck() };
}

我们继续查看 ComponentScanPackageCheck ,由于篇幅受限,Huazie 贴下截图:

在这里插入图片描述

ComponentScanPackageCheckConfigurationWarningsApplicationContextInitializer 中的一个静态内部类,它的目的是在Spring Boot 应用启动时,检查 @ComponentScan 的使用情况,确保没有错误或不推荐的配置方式。通过 ComponentScanPackageCheckgetWarning 方法的检查,如果发现了不恰当的 @ComponentScan 使用,它会生成相应的警告信息,帮助开发者及时发现并修正潜在的配置问题。

下面我们可以来分析下 ConfigurationWarningsPostProcessor,如下截图:

在这里插入图片描述

该类也是一个静态内部类,它同时实现了 PriorityOrderedBeanDefinitionRegistryPostProcessor 接口:

  • PriorityOrdered :实现该接口是用于提高其在多个 BeanFactoryPostProcessor 处理中的执行优先级。
  • BeanDefinitionRegistryPostProcessor:它是对 BeanFactoryPostProcessor 的扩展,允许在常规的 BeanFactoryPostProcessor 检测启动之前注册更多的 bean 定义,这些定义反过来定义了BeanFactoryPostProcessor 实例【可查看 PostProcessorRegistrationDelegate 了解】。

从上述截图中,我们可以看到 postProcessBeanFactory 方法【BeanFactoryPostProcessor 接口定义的方法】是空实现,而postProcessBeanDefinitionRegistry 方法【BeanDefinitionRegistryPostProcessor 接口定义的方法】里,遍历了 checks 数组中的每个检查项,并调用 check.getWarning(registry) 方法获取警告信息。如果警告信息不为空,则调用私有方法 warn(message) 打印警告信息。

3.1.2 ContextIdApplicationContextInitializer

先来看看 ContextIdApplicationContextInitializer 的源码,如下:

在这里插入图片描述
在这里插入图片描述

通过阅读上述源码,可以看出 ContextIdApplicationContextInitializer 是一个用于设置 Spring ApplicationContext ID 的应用上下文初始化器。其中,spring.application.name 属性用于创建 ID。如果该属性未设置,则使用 application

我们在 initialize 方法中,还看到了如下的代码:

applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);

这里就是将一个名为 ContextId 的类注册为单例对象,并将其存储在 SpringApplicationContext 中。然后我们就可以在应用程序的不同部分共享和重用同一个 ContextId 实例,而无需每次都创建新的实例。

3.1.3 DelegatingApplicationContextInitializer

同样先来看看 DelegatingApplicationContextInitializer 的源码,如下截图:

在这里插入图片描述

通过阅读该类的 initialize 方法,我们可以看出 DelegatingApplicationContextInitializer 初始化工作是委托给其他在 context.initializer.classes 环境属性下指定的应用上下文初始化器进行的。

下面的 3.3 小节,我们在自定义 ApplicationContext 初始化器实现 时就会用到。

3.1.4 RSocketPortInfoApplicationContextInitializer

无需多言,直接查看 RSocketPortInfoApplicationContextInitializer 的源码,如下:

public class RSocketPortInfoApplicationContextInitializerimplements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {applicationContext.addApplicationListener(new Listener(applicationContext));}// 省略。。。
}

阅读上述的 initialize 方法,可以看到这里向应用上下文中添加了一个 ApplicationListener,而这个 ListenerRSocketPortInfoApplicationContextInitializer 中的一个静态内部类。

继续阅读 Listener 的源码:

在这里插入图片描述

Listener 是用来监听 RSocketServerInitializedEvent 事件,该事件是在应用程序上下文刷新且 RSocketServer 准备就绪后发布的。

继续查看 onApplicationEvent 方法,我们可以看出该监听器是用来设置 RSocketServer 服务器实际监听的端口的环境属性。属性 local.rsocket.server.port 可以直接使用 @Value 注入到测试中,也可以通过 Environment 获取。另外该属性会自动向上传播到任何父上下文。

3.1.5 ServerPortInfoApplicationContextInitializer

同样还是从 ServerPortInfoApplicationContextInitializer 源码入手,如下所示:

在这里插入图片描述

通过阅读上面的 initialize 方法,可以看到这里也是比较简单,直接向应用上下文中添加了一个 ApplicationListener ,当然这个应用事件监听器比较特殊,就是其本身,因为 ServerPortInfoApplicationContextInitializer 实现了 ApplicationListener 接口。

ApplicationListener 监听的事件是 WebServerInitializedEvent,它是一个在 WebServer 准备就绪时发布的事件。

我们继续阅读 onApplicationEvent 方法的源码:

在这里插入图片描述

我们来简单总结如下:

该应用事件监听器用于设置 WebServer 服务器实际监听的端口的环境属性。属性 local.server.port 【如果 WebServerInitializedEvent 有一个服务器命名空间,它将被用来构造属性名称。例如,“management” actuator 上下文将具有属性名称 local.management.port】可以直接使用 @Value 注入到测试中,也可以通过 Environment 获取。该属性同样会自动向上传播到任何父上下文。

ActuatorSpring Boot 提供的一个开发库,它允许开发人员在运行时监控和管理应用程序。通过 Actuator,你可以查看应用程序的运行状况、性能指标、日志信息等。同时,它也提供了一些内置的管理端点,如健康检查、环境信息、应用信息等,方便开发人员进行调试和监控。Actuator 还提供了扩展机制,允许你自定义管理端点,以满足特定的需求。

3.2 spring-boot-autoconfigure 子模块中内置的实现类

同样我们先看截图:

在这里插入图片描述

从上图中可以看出,spring-boot-autoconfigure 子模块中配置的 ApplicationContextInitializer 实现有 2 个,下面来简单介绍下:

3.2.1 SharedMetadataReaderFactoryContextInitializer

SharedMetadataReaderFactoryContextInitializer 是一个应用上下文初始化器,主要作用是在 Spring 应用程序上下文创建之初,初始化一个共享的 MetadataReaderFactory 实例到在 Spring 应用上下文中。这样,在整个应用程序生命周期内,不同的组件在需要读取类的元数据时,都可以使用一个共享的 MetadataReaderFactory 实例,而无需每次都创建新的实例。

Spring 中,元数据(metadata)是用来描述 Bean 信息的数据,例如类名、方法名、属性名等。在应用程序运行时,Spring 会读取这些元数据来创建和管理 Bean。而 MetadataReaderFactory 就是负责读取和解析类的元数据,比如注解、类属性等。

这块的逻辑比较复杂,Huazie 后续将再出一篇博文详细分析,敬请期待!

3.2.2 ConditionEvaluationReportLoggingListener

ConditionEvaluationReportLoggingListener 是一个用于将 ConditionEvaluationReport 写入日志的应用上下文初始化器,该应用上下文初始化器并不打算在多个应用程序上下文实例之间共享。

Spring 应用程序上下文初始化时,它会评估所有使用条件注解的 bean 定义和配置。这些条件可能基于类是否存在、特定的属性设置、其他 bean 是否存在等。ConditionEvaluationReport 记录了每个条件注解的评估结果,包括哪些条件通过了(即 bean 或配置被创建或执行了),哪些条件没有通过(即 bean 或配置被跳过了)。

ConditionEvaluationReport 的评估结果报告默认将以 DEBUG 级别进行记录。崩溃报告会触发 info 级别的输出,建议再次运行并启用 debug 级别以显示报告。

这块的逻辑也比较复杂,Huazie 后续也会出一篇博文详细介绍下,大家可以期待一下。

3.3 自定义应用上下文初始化器实现

上面 Huazie 同大家一起分析了 Spring Boot 中一些内置的应用上下文初始化器实现,相信对于如何实现 ApplicationContextInitializer 接口,已经有了较为深入的了解。

3.3.1 定义 DemoApplicationContextInitializer

那下面就让我们自定义 ApplicationContext 初始化器实现,如下所示:

public class DemoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {private int order = 0;@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {User user = new User("Huazie", 18);applicationContext.getBeanFactory().registerSingleton(User.class.getName(), user);}public void setOrder(int order) {this.order = order;}@Overridepublic int getOrder() {return this.order;}}

上述 DemoApplicationContextInitializerinitialize 方法中,我们注册了一个 User 类的单例 Bean

3.3.2 添加 DemoApplicationContextInitializer

现在自定义的应用上下文初始化器有了,我们该如何添加它呢?

通过阅读 SpringApplication 的源码 和 本篇 3.1.3 小节的介绍,我们可以总结如下的三种方式:

  • META-INF/spring.factories 中添加 org.springframework.context.ApplicationContextInitializer 的配置。这种方式,我们从 《ApplicationContextInitializer 详解》 的 3.2 小节可见一斑。

    org.springframework.context.ApplicationContextInitializer=com.example.demo.DemoApplicationContextInitializer
    
  • 通过 SpringApplication 中的 addInitializers 方法添加。其实这里在笔者的《SpringApplication 的定制化介绍》中的 1.6 小节也提及过。

    SpringApplication springApplication = new SpringApplication(DemoApplication.class);
    springApplication.addInitializers(new DemoApplicationContextInitializer());
    // 其他省略。。。
    
  • application.properties 中添加 context.initializer.classes 的属性配置。这里实际上来源于 3.1.3 小节的 DelegatingApplicationContextInitializer

    # 逗号分隔的类名列表
    context.initializer.classes=com.example.demo.DemoApplicationContextInitializer
    

    application.yml 中添加 context.initializer.classes 的属性配置

    # 在 YAML 中,数组或列表元素使用 - 符号来定义
    context:  initializer:  classes:  - com.example.demo.DemoApplicationContextInitializer  
    

3.3.3 实际演示

我们采用第三种添加方式,配置截图如下:

在这里插入图片描述

添加如下自测类【用来演示获取在 DemoApplicationContextInitializer 中注册的 User 类的单例 Bean 对象】:

import com.example.demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class DemoApplicationTests {@Autowiredprivate User user;@Testpublic void test() {System.out.println("User = " + user);}
}

我们来看看运行结果,如下所示:

在这里插入图片描述
从上图可以看出,我们自定义的应用上下文初始化器实现显然已经执行了,并且成功注册了 User 类的单例 Bean 对象。

四、总结

本篇 Huazie 带大家一起分析了 Spring Boot 中预置的应用上下文初始化器实现,然后自定义了一个应用上下文初始化器实现类,进一步加深了对 Spring Boot 初始化应用上下文过程的了解,为后续的启动运行过程的理解打下了坚实的基础。

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

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

相关文章

【智能算法】斑鬣狗优化算法(SHO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过。 3.代码实现4.参考文献 1.背景 2017年&#xff0c;Dhiman等人受到斑鬣狗自然狩猎行为启发&#xff0c;提出了斑鬣狗优化算法(Spotted Hyena Optimizer, SHO)。 2.算法原理 2.1算法思想 SHO将斑鬣狗狩猎行为分为围捕-狩猎-进攻三…

Python入门(三)

序列 序列是有顺序的数据集合。序列包含的一个数据被称为元素&#xff0c;序列可以由一个或多个元素组成&#xff0c;也是可以没有任何元素的空序列。 序列的类型 元组&#xff08;定值表&#xff09;&#xff1a;一旦建立&#xff0c;各个元素不可再更变&#xff0c;所以一…

Wireshark抓包工具的使用

提示&#xff1a;本文为学习记录&#xff0c;若有错误&#xff0c;请联系作者&#xff0c;谦虚受教 文章目录 前言一、下载二、首页三、使用1.读入数据2.分析数据3.筛选IP4.保存数据 四、过滤器表达式五、TCP总结 前言 低头做事&#xff0c;抬头看路。 一、下载 下载路径wire…

【论文笔记合集】LSTNet之循环跳跃连接

本文作者&#xff1a; slience_me LSTNet 循环跳跃连接 文章仅作为个人笔记 论文链接 文章原文 LSTNet [25] introduces convolutional neural networks (CNNs) with recurrent-skip connections to capture the short-term and long-term temporal patterns. LSTNet [25]引入…

【Java刷题篇】串联所有单词的子串

这里写目录标题 &#x1f4c3;1.题目&#x1f4dc;2.分析题目&#x1f4dc;3.算法原理&#x1f9e0;4.思路叙述✍1.进窗口✍2.判断有效个数✍3.维护窗口✍4.出窗口 &#x1f4a5;5.完整代码 &#x1f4c3;1.题目 力扣链接: 串联所有单词的子串 &#x1f4dc;2.分析题目 阅…

口腔管理平台 |基于springboot框架+ Mysql+Java+B/S结构的口腔管理平台 设计与实现(可运行源码+数据库+lw文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 会员功能 系统功能设计 数据库E-R图设计 lunwen参考…

智慧公厕建设的主要目标是什么?

随着城市化进程的不断推进&#xff0c;公共厕所作为城市基础设施的重要组成部分&#xff0c;也变得越来越重要。为了提升公共厕所的管理水平、提供更好的服务质量&#xff0c;智慧公厕应运而生。智慧公厕的建设旨在通过信息化手段实现公共厕所的全面感知监测&#xff0c;实现公…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:FormLink)

提供静态卡片交互组件&#xff0c;用于静态卡片内部和提供方应用间的交互&#xff0c;当前支持router、message和call三种类型的事件。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 该组件仅可…

6.【Linux】进程间通信(管道命名管道||简易进程池||简易客户端服务端通信)

介绍 进程间通信的方式 1.Linux原生支持的管道----匿名和命名管道 2.System V-----共享内存、消息队列、信号量 3.Posix------多线程、网路通信 进程间通信目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。…

【C++刷题】优选算法——动态规划第一辑

1.状态表示是什么&#xff1f;简答理解是dp表里的值所表示的含义怎么来的&#xff1f;题目要求经验题目要求分析问题的过程中&#xff0c;发现重复子问题 2.状态转移方程dp[i]......细节问题&#xff1a;3.初始化控制填表的时候不越界4.填表顺序控制在填写当前状态的时候&#…

腾讯云免费服务器配置大全和个人企业申请流程,2024年新版教程

腾讯云免费服务器申请入口 https://curl.qcloud.com/FJhqoVDP 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云服务器网txyfwq.com分享2024年最新腾讯云免费…

蓝桥杯每日一题——棋盘

问题描述 小蓝拥有 n xn 大小的棋盘&#xff0c;一开始棋盘上全都是白子。小蓝进行了 m 次操作&#xff0c;每次操作会将棋盘上某个范围内的所有棋子的颜色取反(也就是白色棋子变为黑色&#xff0c;黑色棋子变为白色)请输出所有操作做完后棋盘上每个棋子的颜色。输入格式 输入的…

卷积神经网络算法原理(卷积层,卷积运算,填充,步幅,经过卷积运算后的特征图大小,池化层,最大池化,平均池化,经过池化层运算后的特征图大小)

文章目录 卷积神经网络算法原理(卷积层&#xff0c;卷积运算&#xff0c;填充&#xff0c;步幅&#xff0c;经过卷积运算后的特征图大小&#xff0c;池化层&#xff0c;最大池化&#xff0c;平均池化&#xff0c;经过池化层运算后的特征图大小)前言1、图像的本质1.1、灰度图1.2…

Java学习笔记(16)

常见算法 查找算法 查询某个元素是否存在 二分查找&#xff08;数组元素必须是有序的&#xff09; package exercise;public class exercise1 {public static void main(String[] args) {int[] arr {7, 23, 797, 23, 79, 81, 103, 127, 131, 147};System.out.println(binar…

实战Python Socket编程:开发多用户聊天应用

实战Python Socket编程&#xff1a;开发多用户聊天应用 Python Socket 编程概述什么是Socket编程&#xff1f;Socket编程的应用场景Socket编程的重要性基本概念 环境准备Python版本必要的库开发环境配置调试工具 基本Socket编程创建Socket绑定Socket到端口监听连接接受连接发送…

24.第12届蓝桥杯省赛真题题解

A.空间&#xff08;100%&#xff09; 计算机存储单位计算 1TB2^10 GB 1GB2^10 MB 1MB2^10 KB 1KB2&10 B 1B8 bit(bit位二进制的最小的存储单位) #include <iostream> #include <cmath>using namespace std; //2^28B 2^2int main(){std::ios::sync_with_stdio…

【C语言入门】浮点型数据在内存中的存储

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;C语言 个人主页&#xff1a;Celias blog~ 目录 ​编辑 引言 引例 一、浮点型在内存中的存储方式 1.1 …

在idea中配置tomcat服务器,然后部署一个项日

1.下载tomcat Tomcat下载 点击右边的tomcat8 找到zip点击下载 下载完&#xff0c;解压到你想放置的路径下 2.配置环境变量 打开设置找到高级系统设置点击环境变量 点击新建&#xff0c;变量名输入&#xff1a;CATALINA_HOME&#xff0c;变量值就是Tomcat的安装路径&#x…

【QT+QGIS跨平台编译】之七十七:【QGIS_Gui跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件一、字符串错误 常量中有换行符错误:(也有const char * 到 LPCWSTR 转换的错误) 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 src\gui\qgsadvanceddigitizingdockwidge…

智慧礼金:电子礼金薄,让礼薄更添智能,你确定不进来看看?

智慧礼金&#xff1a;电子礼金薄&#xff0c;让礼薄更添智能&#xff0c;你确定不进来看看&#xff1f; 一、重要声明二、相关介绍三、使用好处四、如何找到该小程序 随着科技的不断进步&#xff0c;传统的纸质礼金簿已经逐渐被电子化管理所取代。今天&#xff0c;我们要向大家…