【Spring】SpringBoot的扩展点之ApplicationContextInitializer
简介
其实spring启动步骤中最早可以进行扩展的是实现ApplicationContextInitializer接口。来看看这个接口的注释。
package org.springframework.context;/*** Callback interface for initializing a Spring {@link ConfigurableApplicationContext}* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.** <p>Typically used within web applications that require some programmatic initialization* of the application context. For example, registering property sources or activating* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support* for declaring a "contextInitializerClasses" context-param and init-param, respectively.** <p>{@code ApplicationContextInitializer} processors are encouraged to detect* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been* implemented or if the {@link org.springframework.core.annotation.Order @Order}* annotation is present and to sort instances accordingly if so prior to invocation.** @author Chris Beams* @since 3.1* @param <C> the application context type* @see org.springframework.web.context.ContextLoader#customizeContext* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers*/
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {/*** Initialize the given application context.* @param applicationContext the application to configure*/void initialize(C applicationContext);}
简要的说明一下,有这么几点:
- 实现这个接口之后,它的initialize方法会在容器
ConfigurableApplicationContext刷新之前触发。 - 它通常用于在容器初始化之前进行一些程序上的操作,比如说注册一些环境变量或者读取一些配置文件。
- 它可以使用@Order指定优先级
实现方式
它有三种实现方式:
- 通过SPI机制实现,在
resources/META-INF/spring.factories中定义如下内容:
org.springframework.context.ApplicationContextInitializer=com.alone.spring.aop.demo.config.ContextInitializerTest
/*** spring扩展点 ApplicationContextInitializer*/
@Slf4j
public class ContextInitializerTest implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {log.info("ContextInitializerTest 开始加载");ConfigurableEnvironment environment = applicationContext.getEnvironment();Map<String, Object> initMap = new HashMap<>();initMap.put("20231116", "This is init");MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);environment.getPropertySources().addLast(propertySource);log.info("ContextInitializerTest 加载结束");}
}
- 在
application.yml中定义如下内容:
context:initializer:classes: com.alone.spring.aop.demo.config.YmlApplicationContextInitializer
@Slf4j
public class YmlApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {log.info("这是yml的ApplicationContextInitializer");ConfigurableEnvironment environment = applicationContext.getEnvironment();Map<String, Object> initMap = new HashMap<>();initMap.put("20231116", "YmlApplicationContextInitializer");MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);environment.getPropertySources().addLast(propertySource);log.info("YmlApplicationContextInitializer 加载结束");}
}
- 在启动类中进行注册:
public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);springApplication.addInitializers(new MainFlagApplicationContextInitializer());springApplication.run();
}
@Component
@Slf4j
public class MainFlagApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {log.info("这是main的ApplicationContextInitializer");ConfigurableEnvironment environment = applicationContext.getEnvironment();Map<String, Object> initMap = new HashMap<>();initMap.put("20231116", "MainFlagApplicationContextInitializer");MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);environment.getPropertySources().addLast(propertySource);log.info("MainFlagApplicationContextInitializer 加载结束");}
}
三者的加载顺序是:
application.yml >spring.factories >启动类

源码分析
从启动类的new SpringApplication(SpringbootApplication.class)开始分析:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}
看到上面第8行(源码266行)中出现了ApplicationContextInitializer.class猜想它肯定是在读取相关的配置,跟进去发现出现了下面这行。

这里是读取了spring.factories中的内容,但看它的结果发现不止我们自定义的类一个,说明springboot内置了一些ApplicationContextInitializer,后续我们再看它们具体的作用,这里先截图列出按下不表。

然后沿如下的调用栈可以找到initializer.initialize(context);这一行调用ApplicationContextInitializer的语句。
● springApplication.run()
● run:306, SpringApplication (org.springframework.boot)
● prepareContext:383, SpringApplication (org.springframework.boot)
● applyInitializers:614, SpringApplication (org.springframework.boot)
框起来的方法会对所有的initializer进行排序,排序后的结果见左边。
在执行到DelegatingApplicationContextInitializer时会去读取环境中的context.initializer.classes,也就是application.yml中配置的内容执行。所以会先执行yml配置的initializer.

以上总结一下是这样的:

大致调用的流程图是:

系统内置初始化类
最后我们来看看上面提到的系统内置的初始化类都有些什么作用。
SharedMetadataReaderFactoryContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);applicationContext.addBeanFactoryPostProcessor(postProcessor);
}
初始化了一个CachingMetadataReaderFactoryPostProcessor至容器中
DelegatingApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext context) {ConfigurableEnvironment environment = context.getEnvironment();List<Class<?>> initializerClasses = getInitializerClasses(environment);if (!initializerClasses.isEmpty()) {applyInitializerClasses(context, initializerClasses);}
}
执行context.initializer.classes配置的initializer。
ContextIdApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {ContextId contextId = getContextId(applicationContext);applicationContext.setId(contextId.getId());applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
}private ContextId getContextId(ConfigurableApplicationContext applicationContext) {ApplicationContext parent = applicationContext.getParent();if (parent != null && parent.containsBean(ContextId.class.getName())) {return parent.getBean(ContextId.class).createChildId();}return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}private String getApplicationId(ConfigurableEnvironment environment) {String name = environment.getProperty("spring.application.name");return StringUtils.hasText(name) ? name : "application";
}
设置容器的id,值取自spring.application.name配置,默认是application
ConditionEvaluationReportLoggingListener
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {this.applicationContext = applicationContext;applicationContext.addApplicationListener(new ConditionEvaluationReportListener());if (applicationContext instanceof GenericApplicationContext) {// Get the report early in case the context fails to loadthis.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());}
}
注册了一个ConditionEvaluationReportListener
RestartScopeInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {applicationContext.getBeanFactory().registerScope("restart", new RestartScope());
}
自动重启相关。
ConfigurationWarningsApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext context) {context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
初始化一个ConfigurationWarningsPostProcessor用于记录公共的容器配置错误信息。
RSocketPortInfoApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {applicationContext.addApplicationListener(new Listener(applicationContext));
}
增加了一个监听器用于监听RSockerServer的端口是否正常。

ServerPortInfoApplicationContextInitializer
/*** {@link ApplicationContextInitializer} that sets {@link Environment} properties for the* ports that {@link WebServer} servers are actually listening on. The property* {@literal "local.server.port"} can be injected directly into tests using* {@link Value @Value} or obtained via the {@link Environment}.* <p>* If the {@link WebServerInitializedEvent} has a* {@link WebServerApplicationContext#getServerNamespace() server namespace} , it will be* used to construct the property name. For example, the "management" actuator context* will have the property name {@literal "local.management.port"}.* <p>* Properties are automatically propagated up to any parent context.** @author Dave Syer* @author Phillip Webb* @since 2.0.0*/@Override
public void initialize(ConfigurableApplicationContext applicationContext) {applicationContext.addApplicationListener(this);
}
向容器中增加一个监听器用于检测WebServer的端口是否正常监听。
参考资料
- SpringBoot系统初始化器使用及源码解析(ApplicationContextInitializer)
- 跟我一起阅读SpringBoot源码(九)——初始化执行器
- Springboot扩展点之ApplicationContextInitializer
相关文章:
【Spring】SpringBoot的扩展点之ApplicationContextInitializer
简介 其实spring启动步骤中最早可以进行扩展的是实现ApplicationContextInitializer接口。来看看这个接口的注释。 package org.springframework.context;/*** Callback interface for initializing a Spring {link ConfigurableApplicationContext}* prior to being {linkpl…...
哈希表HashTable
散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。 哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,复杂度O(1) 哈希表本质…...
【软件测试】一位优秀测试工程师具备哪些知识和经验?
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 根据观察…...
MongoDB相关基础操作(库、集合、文档)
文章目录 一、库的相关操作1、查看数据库2、查看当前库3、创建数据库4、删除数据库 二、集合的相关操作1、查看库中所有集合2、创建集合2.1、显示创建2.2、隐式创建 3、删除集合 三、文档的相关操作1、插入文档1.1、插入单条文档1.2、插入多条文档1.3、脚本方式 2、查询文档3、…...
进程和线程( Process and Thread)
目录 一、操作系统(Operating System) 操作系统的定位 二、 什么是进程/任务(Process/Task) 1.进程控制块抽象(PCB Process Control Block) 2.PCB中重要的属性 3.并发编程 三、线程(Thread) 1. 线程是…...
linux apache安装及虚拟主机配置
centos 安装apache, yum install httpd 将httpd.conf中的ServerName 前面的 # 去掉。 apache 2.2 的虚拟机的配置放置在conf/extra/httpd-vhosts.conf 中 apache 虚拟主机设置 a2enmod rewrite sudo vim 000-default 修改 ServerName *:80 <VirtualHost *…...
基于Spring Boot 框架的试卷自动生成系统的设计与实现
项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。你想解决的问题,今天给大家介绍…...
开发《猫咪攻略》小游戏的意义
开发《猫咪攻略》小游戏的意义有以下几点: 学习和掌握游戏开发的基本技能:通过开发《猫咪攻略》小游戏,可以学习和掌握游戏开发的基本技能,包括游戏策划、游戏设计、游戏编程和游戏测试等方面的技能。增强对猫咪的了解和认识&…...
hadoop、hive、DBeaver的环境搭建及使用
本文主要介绍hadoop、hive的结构及使用,具体的操作步骤见最后的附件; hadoop提供大数据的存储、资源调度、计算,分为三个模块:HDFS、YRAN、MapReduce HDFS提供数据的分布式存储,分为三个节点NameNode,DataNode,Second…...
Linux上通过SSL/TLS和start tls连接到LDAP服务器(附C++代码实现认证流程)
一,大致流程。 1.首先在Linux上搭建一个LDAP服务器 2.在LDAP服务器上安装CA证书,服务器证书,因为SSL/TLS,start tls都属于机密通信,需要客户端和服务器都存在一个相同的证书认证双方的身份。3.安装phpldapadmin工具&am…...
HarmonyOS ArkTS List组件和Grid组件的使用(五)
简介 ArkUI提供了List组件和Grid组件,开发者使用List和Grid组件能够很轻松的完成一些列表页面。常见的列表有线性列表(List列表)和网格布局(Grid列表): List组件的使用 List是很常用的滚动类容器组件&…...
考研思想政治理论大纲
一:马克思主义基本原理概论 (一)马克思主义是关于无产阶级和人类解放的科学 1、马克思主义的创立和发展 马克思主义的含义。马克思主义产生的经济社会根源、实践基础和思想渊源、马克思主义的创立、马克思主义在实践中的发展 2、马克思主义的鲜明特征 马克思主义科学性和革命…...
日期格式转化成星期几部署到linux显示英文
异常收集 原因:解决办法仰天大笑出门去,我辈岂是蓬蒿人 传入一个时间获取这个时间对应的是星期几,在开发环境(window系统)中显示为星期几,部署到服务器(linux系统)中会显示英文的时间…...
一个关于proto 文件的经验分享 :gRPC 跨语言双端通信显示错误码:12 UNIMPLEMENTED (附赠gRPC错误码表)
错误现象描述: 在使用c的客户端向golang的服务端发送远程调用时,显示: /home/zry/gRPC/grpc-v1.45.2/examples/cpp/DeviceData/greeter_client.cc83 12: unknown service DeviceData.DeviceDataService Greeter 接收到: RPC 失败这里的unkn…...
腾讯极光盒子A4021增强版_线刷官方
1、用USB_Burning_Tool线刷提供的线刷包,所需资料地址在最后 1)打开USB_Burning_Tool,选择资料里的A4021_line_flash_root.img(文件夹最好没有中文字符和空格),然后点击【开始】。 2)盒子准备好双USB线和电源。在1秒内先插入电源,再插入usb口。等这个软件识别到盒子,…...
机器学习第11天:降维
文章目录 机器学习专栏 主要思想 主流方法 投影 二维投射到一维 三维投射到二维 流形学习 PCA主成分分析 介绍 代码 内核PCA 具体代码 LLE 结语 机器学习专栏 机器学习_Nowl的博客-CSDN博客 主要思想 介绍:当一个任务有很多特征时,我们…...
异步爬取+多线程+redis构建一个运转丝滑且免费http-ip代理池 (三)
内容提要: 如果说,爬取网页数据的时候,我们使用了异步,那么将数据放入redis里面,其实也需要进行异步;当然,如果使用多线程或者redis线程池技术也是可以的,但那会造成冗余; 因此,在测试完多线程redis搭配异步爬虫的时候,我发现效率直接在redis这里被无限拉低下来! 因此: 最终的r…...
VSCode新建Vue项目
前言 Vue.js 是一款流行的 JavaScript 前端框架,它可以帮助开发者轻松构建高性能、可扩展的 Web 应用程序。而 VSCode 则是一款功能强大的开源代码编辑器,它提供了许多有用的工具和插件,可以大幅提高开发效率。 在本文中,我们将…...
前端学习--React(1)
一、React简介 React由Meta公司研发,是一个用于 构建Web和原生交互界面的库 优势:组件化开发、不错的性能、丰富生态(所有框架中最好)、跨平台(web、ios、安卓) 开发环境搭建 打开相应文件夹 新建终端并…...
HarmonyOS从基础到实战-高性能华为在线答题元服务
最近看到美团、新浪、去哪儿多家互联网企业启动鸿蒙原生应用开发,这个HarmonyOS NEXT越来越引人关注。奈何当前不面向个人开发者开放,但是我们可以尝试下鸿蒙新的应用形态——元服务的开发。 元服务是基于HarmonyOS提供的一种面向未来的服务提供方式&…...
代码注释翻译工具ccmate:精准解析与翻译,提升跨语言编程效率
1. 项目概述:一个为开发者设计的代码片段翻译工具如果你和我一样,经常需要查阅、学习或者借鉴一些来自不同语言社区的代码,比如在GitHub上看到一个很棒的Python库,但它的文档和注释全是日文;或者想快速理解一段用西班牙…...
开源技能库:结构化技能体系如何驱动个人与团队技术成长
1. 项目概述:一个开源技能库的诞生与价值在技术社区里,我们常常会遇到这样的场景:一个刚入行的开发者,面对琳琅满目的技术栈感到迷茫,不知道从何学起;一个经验丰富的工程师,想要系统性地梳理自己…...
机器学习高效工作流:ml-retreat深度工作法实战指南
1. 项目概述:当机器学习遇上“静修”最近在GitHub上闲逛,发现了一个挺有意思的项目,叫hesamsheikh/ml-retreat。初看这个标题,你可能会有点摸不着头脑:“ml”是机器学习(Machine Learning)没跑&…...
基因数据交易模拟平台:用金融市场模型探索基因组学动态分析
1. 项目概述:一个基因数据交易与分析的实验平台最近在GitHub上看到一个挺有意思的项目,叫“genome-trader-lab”。光看名字,你可能会觉得有点跨界——“genome”(基因组)和“trader”(交易者)这…...
Linux xargs 命令深度解析:从管道到命令构建的桥梁
在 Linux 终端里,管道符 | 可以说是最常用的操作符了。但很多人遇到过这种情况:管道前面的命令输出了一堆文件名,想传给后面的命令处理,结果报错了。 # 删除所有 .log 文件 find . -name "*.log" | rm rm: missing ope…...
Azure/setup-helm:GitHub Actions 中 Helm 客户端安装的标准化解决方案
1. 项目概述:为什么我们需要一个官方的 Helm 安装 Action?如果你在 GitHub Actions 的工作流里用过 Helm,大概率经历过这样的场景:为了安装 Helm 客户端,你不得不在steps里写一段run命令,可能是从 GitHub R…...
数字孪生如何破解AI预测性维护的可解释性与泛化难题
1. 项目概述:当数字孪生遇见AI预测性维护在工业界摸爬滚打十几年,我亲眼见证了维护策略从“坏了再修”到“定期保养”,再到如今炙手可热的“预测性维护”的演进。预测性维护(Predictive Maintenance, PMx)的核心愿景很…...
CANN Cumsum算子测试题
决赛题目:Cumsum 算子测试用例设计 【免费下载链接】cann-competitions 本仓库用于 CANN 开源社区各类竞赛、开源课题、社区任务等课题发布、开发者作品提交和展示。 项目地址: https://gitcode.com/cann/cann-competitions 任务说明 本题目要求参赛者为 CA…...
数据驱动的可解释AI:从特征归因到样本影响分析的实践指南
1. 项目概述:当数据挖掘遇见可解释AI在深度学习的浪潮席卷了几乎所有领域之后,我们获得了一个又一个性能惊人的“黑箱”模型。作为一名长期在数据科学一线工作的从业者,我见证了模型精度从90%提升到99.9%的激动,也亲历了当业务方或…...
cocos2d-iPhone
1.cocos2d-iphone环境搭建 第一步:下载cocos2d-iphone最新版本;地址:http://code.google.com/p/cocos2d-iphone/downloads/list (当前最新2.1-beta) 第二步:下载完成之后解压&#x…...
