【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);}

简要的说明一下,有这么几点:

  1. 实现这个接口之后,它的initialize方法会在容器ConfigurableApplicationContext刷新之前触发。
  2. 它通常用于在容器初始化之前进行一些程序上的操作,比如说注册一些环境变量或者读取一些配置文件。
  3. 它可以使用@Order指定优先级

实现方式

它有三种实现方式:

  1. 通过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 加载结束");}
}
  1. 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 加载结束");}
}
  1. 在启动类中进行注册:
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的端口是否正常监听。

参考资料

  1. SpringBoot系统初始化器使用及源码解析(ApplicationContextInitializer)
  2. 跟我一起阅读SpringBoot源码(九)——初始化执行器
  3. Springboot扩展点之ApplicationContextInitializer

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

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

相关文章

ChatGPT规模化服务的经验与教训

2022年11月30日&#xff0c;OpenAI发布ChatGPT&#xff0c;以很多人未曾预料的速度迅速走红。与此同时&#xff0c;由于短时间内用户量的暴涨&#xff0c;导致服务器过载&#xff0c;迫使OpenAI停止新用户的注册。 ChatGPT发布这一年&#xff0c;同样的情景发生了好几次。在最近…

暖阳脚本_ 将Agent技术的灵活性引入RPA,清华等发布自动化智能体ProAgent

RPA暖阳脚本 近日&#xff0c;来自清华大学的研究人员联合面壁智能、中国人民大学、MIT、CMU 等机构共同发布了新一代流程自动化范式 “智能体流程自动化” Agentic Process Automation&#xff08;APA&#xff09;&#xff0c;结合大模型智能体帮助人类进行工作流构建&#x…

微信小程序会议OA-登录获取手机号流程登录-小程序导入微信小程序SDK(从微信小程序和会议OA登录获取手机号到登录小程序导入微信小程序SDK)

目录 获取用户昵称头像和昵称 wx.getUserProfile bindgetuserinfo 登录过程 登录-小程序 wx.checkSession wx.login wx.request 后台 准备数据表 反向生成工具生成 准备封装前端传过来的数据 小程序服器配置 导入微信小程序SDK application.yml WxProperties …

ABAP调用Https接口 Ssl证书导入

ABAP调用Https接口 Ssl证书导入 一、证书导入 谷歌浏览器打开对方系统URL地址&#xff0c;下载SSL Server certificate,步骤如下&#xff1a; 浏览器打开要导出certificate(证书)的网站&#xff0c;点击这个小锁的图标&#xff1a; 点击连接是安全的后面小播放按钮 点击证…

解决ElementUI时间选择器回显出现Wed..2013..中国标准时间.

使用饿了么组件 时间日期选择框回显到页面为啥是这样的&#xff1f; 为什么再时间框中选择日期&#xff0c;回显页面出现了这种英文格式呢&#xff1f;&#xff1f;&#xff1f;&#xff1f; 其实这个问题直接使用elementui的内置属性就能解决 DateTimePicker 日期时间选择…

汇编-PROTO声明过程

64位汇编 64 模式中&#xff0c;PROTO 伪指令指定程序的外部过程&#xff0c;示例如下&#xff1a; ExitProcess PROTO ;指定外部过程&#xff0c;不需要参数.code main PROCmov ebx, 0FFFFFFFFh mov ecx,0 ;结束程序call ExitProcess ;调用外部过程main ENDP END 32位…

【华为OD题库-032】数字游戏-java

题目 小明玩一个游戏。系统发1n张牌&#xff0c;每张牌上有一个整数。第一张给小明&#xff0c;后n张按照发牌顺序排成连续的一行。需要小明判断&#xff0c;后n张牌中&#xff0c;是否存在连续的若干张牌&#xff0c;其和可以整除小明手中牌上的数字. 输入描述: 输入数据有多组…

redis---非关系型数据库

关系数据库与非关系型数据库 redis非关系型数据库&#xff0c;又名缓存型数据库。数据库类型&#xff1a;关系型数据库和非关系型数据库关系型数据库是一 个机构化的数据库,行和列。 列&#xff1a;声明对象。 行&#xff1a;记录对象属性。 表与表之间的的关联。 sql语句&…

实验7设计建模工具的使用(三)

二&#xff0c;实验内容与步骤 1. 百度搜索1-2张状态图&#xff0c;请重新绘制它们&#xff0c;并回答以下问题&#xff1a; 1&#xff09;有哪些状态&#xff1b; 2&#xff09;简要描述该图所表达的含义&#xff1b; 要求&#xff1a;所绘制的图不得与本文中其它习题一样…

python django 小程序图书借阅源码

开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…

神经网络中BN层简介及位置分析

1. 简介 Batch Normalization是深度学习中常用的技巧&#xff0c;Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift (Ioffe and Szegedy, 2015) 第一次介绍了这个方法。 这个方法的命名&#xff0c;明明是Standardization, 非…

【考研数学】数学一“背诵”手册(一)| 高数部分(2)

文章目录 引言一、高数级数空间解析几何球坐标变换公式零碎公式 写在最后 引言 高数一篇文章还是写不太下&#xff0c;再分一些到这里来吧 一、高数 级数 阿贝尔定理&#xff1a;若级数 ∑ a n x n \sum a_nx^n ∑an​xn 当 x x 0 xx_0 xx0​ 时收敛&#xff0c;则适合不…

计算机毕业设计选题推荐-点餐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

边缘计算多角色智能计量插座 x 资产显示标签:实现资产追踪与能耗管理的无缝结合

越来越多智慧园区、智慧工厂、智慧医院、智慧商业、智慧仓储物流等企业商家对精细化、多元化智能生态应用场景的提升&#xff0c;顺应国家节能减排、环保的时代潮流&#xff0c;设计一款基于融合以太网/WiFi/蓝牙智能控制的智能多角色插座应运而生&#xff0c;赋予智能插座以遥…

微博头条文章开放接口报错 auth by Null spi

接口文档地址 https://open.weibo.com/wiki/Toutiao/api 接口说明 https://api.weibo.com/proxy/article/publish.json 请求方式 POST 请求参数 参数名称类型是否必需描述titlestring是文章标题&#xff0c;限定32个中英文字符以内contentstring是正文内容&#xff0c;限制9…

竞赛 : 题目:基于深度学习的水果识别 设计 开题 技术

1 前言 Hi&#xff0c;大家好&#xff0c;这里是丹成学长&#xff0c;今天做一个 基于深度学习的水果识别demo 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/pos…

Java 编码

编码: 加密: 通过加密算法和密钥进行 也可通过码表进行加密 对称加密: 缺点:可被截获 元数据---加密算法密钥密文 ----> 解密算法密钥元数据 算法:DES(短 56位),AES(长 128位)破解时间加长 非对称加密: 元数据-加密算法加密密钥 密文 --->加密算法解密密钥元数据 …

轻量封装WebGPU渲染系统示例<38>- 动态构建WGSL材质Shader(源码)

实现原理: 基于宏定义和WGSL文件系统实现(还在完善中...) 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/DynamicShaderBuilding.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript…

高防服务器的工作原理

在当今互联网时代&#xff0c;网络安全问题日益突出&#xff0c;各种网络攻击层出不穷。为了保护企业的网络安全&#xff0c;高防服务器应运而生。那么&#xff0c;你是否了解高防服务器的工作原理呢&#xff1f;下面就让我们一起来探索一下。 高防服务器是一种能够有效抵御各种…

浅谈WPF之各种Template

前几天写了一篇文章【浅谈WPF之控件模板和数据模板】&#xff0c;有粉丝反馈说这两种模板容易弄混&#xff0c;不知道什么时候该用控件模块&#xff0c;什么时候该用数据模板&#xff0c;以及template和itemtemplate之间的关系等&#xff0c;今天专门写一篇文章&#xff0c;简述…