【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.mzph.cn/news/152855.shtml

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

相关文章

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】MobileNets_V2模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】MobileNets_V2模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】MobileNets_V2模型算法详解前言MobleNet_V2讲解反向残差结构(Inverted Residuals)兴趣流形(Manifold of interest)线性瓶颈层…

Postgresql运维信息(一)

1. 运维系统视图 PostgreSQL 提供了一系列系统视图和函数&#xff0c;可以用于获取数据库的运维统计信息。这些信息对于监控和优化数据库性能非常有用。以下是一些常用的 PostgreSQL 运维统计信息&#xff1a; 1.1. pg_stat_activity 这个系统视图包含了当前数据库连接的活动…

智能驾驶汽车虚拟仿真视频数据理解(一)

赛题官网 datawhale 赛题介绍 跑通demo paddle 跑通demo torch 提交的障碍物取最主要的那个&#xff1f;不考虑多物体提交。障碍物&#xff0c;尽可能选择状态发生变化的物体。如果没有明显变化的&#xff0c;则考虑周边的物体。车的状态最后趋于减速、停止&#xff0c;时序…

Java语言基础第四天

笔记&#xff1a; Scanner接收用户输入的数据&#xff1a; package day04; import java.util.Scanner; //1.导入扫描仪 //Scanner结构的演示 public class ScannerDemo {public static void main(String[] args) {Scanner scan new Scanner(System.in); //2.新建一个扫描仪sca…

Ubuntu18.04运行gazebo的launch文件[model-4] process has died报错

启动gazebo仿真环境报错[model-4] process has died [model-4] process has died [pid 2059, exit code 1, cmd /opt/ros/melodic/lib/gazebo_ros/spawn_model -urdf -model mycar -param robot_description __name:model __log:/root/.ros/log/8842dc14-877c-11ee-a9d9-0242a…

如何在 Vue 中使用 防抖 和 节流

在监听频繁触发的事件时&#xff0c;一定要多加小心&#xff0c;比如 用户在输入框打字、窗口大小调整、滚动、Intersection Observer 事件。 这些事件总是被频繁触发&#xff0c;可能 几秒一次。如果针对每次事件都发起 fetch 请求&#xff08;或类似的行为&#xff09;&…

ts学习04-Es5中的类和静态方法 继承

最简单的类 function Person() {this.name "张三";this.age 20; } var p new Person(); console.log(p.name);//张三构造函数和原型链里面增加方法 function Person(){this.name张三; /*属性*/this.age20;this.runfunction(){console.log(this.name在运动);} }…

redis-持久化

目录 一、RDB RDB触发保存的两种方式 优劣势总结 二、AOF AOF持久化流程&#xff1a; 1、开启AOP 2、异常恢复 3、AOF的同步频率设置 4、ReWrite压缩 5、优劣势总结 Redis 4.0 混合持久化 redis是内存数据库&#xff0c;所有的数据都会默认存在内存中&#xff0c;如…

【2024系统架构设计】 系统架构设计师第二版-未来信息综合技术

目录 一 信息物理系统 二 人工智能 三 机器人技术 四 边缘计算 五 数字孪生体

时间序列预测实战(十七)PyTorch实现LSTM-GRU模型长期预测并可视化结果(附代码+数据集+详细讲解)

一、本文介绍 本文给大家带来的实战内容是利用PyTorch实现LSTM-GRU模型&#xff0c;LSTM和GRU都分别是RNN中最常用Cell之一&#xff0c;也都是时间序列预测中最常见的结构单元之一&#xff0c;本文的内容将会从实战的角度带你分析LSTM和GRU的机制和效果&#xff0c;同时如果你…

kubernetes v1.24.7 + docker

1. 背景 采用containerd作为容器运行时发现集群总是不稳定&#xff0c;因此切换成了docker 作为容器运行时&#xff1b; rootorangepi3b:~# uname -a Linux orangepi3b 5.10.160-rockchip-rk356x #1.0.2 SMP Thu Sep 21 17:07:22 CST 2023 aarch64 aarch64 aarch64 GNU/Linux…

论文导读 | 大语言模型与知识图谱复杂逻辑推理

前 言 大语言模型&#xff0c;尤其是基于思维链提示词&#xff08;Chain-of Thought Prompting&#xff09;[1]的方法&#xff0c;在多种自然语言推理任务上取得了出色的表现&#xff0c;但不擅长解决比示例问题更难的推理问题上。本文首先介绍复杂推理的两个分解提示词方法&a…

【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 一.了解项目功能 在本次项目中我们的目标是实现一个带头双向循环链表: 该带头双向循环链表使用动态内存分配空间,可以用来存储任意数量的同类型数据. 带头双向循环链表结点(No…

Windows 安装 Docker Compose

目录 前言什么是 Docker Compose &#xff1f;安装 Docker Compose配置环境变量结语开源项目 前言 在当今软件开发和部署领域&#xff0c;容器化技术的应用已成为提高效率和系统可移植性的关键手段。Docker&#xff0c;作为领先的容器化平台&#xff0c;为开发人员提供了轻松构…

2023 极术通讯-汽车“新四化”路上,需要一片安全山海

导读&#xff1a;极术社区推出极术通讯&#xff0c;引入行业媒体和技术社区、咨询机构优质内容&#xff0c;定期分享产业技术趋势与市场应用热点。 芯方向 【Armv9】-动态TrustZone技术的介绍 动态 TrustZone 是提供多租户安全媒体 pipeline 的绝佳工具。完全不受操作系统、虚…

Java的动态代理Proxy.newProxyInstance

本文参考&#xff1a;java动态代理Proxy.newProxyInstance-CSDN博客 一、动态代理定义 利用Java的反射技术&#xff0c;在运行期间创建可以实现某些给定接口的新类&#xff0c;称为动态代理类。 此处代理的接口&#xff08;Interfaces&#xff09;&#xff0c;不是类(Class&…

CSS中2种复合选择器

1:交集选择器 作用&#xff1a;选中同时符合多个条件的元素 语法&#xff1a;选择器1选择器2选择器n{} 注意&#xff1a;若交集选择器中有元素选择器&#xff0c;必须使用元素选择器开头 2:并集选择器 作用&#xff1a;同时选择多个选择器对应的元素 语法&#xff1a;选择…

矩阵的QR分解

矩阵的QR分解 GramSchmidt 设存在 B { x 1 , x 2 , … , x n } \mathcal{B}\left\{\mathbf{x}_{1},\mathbf{x}_{2},\ldots,\mathbf{x}_{n}\right\} B{x1​,x2​,…,xn​}在施密特正交化过程中 q 1 x 1 ∣ ∣ x 1 ∣ ∣ q_1\frac{x_1}{||x_1||} q1​∣∣x1​∣∣x1​​ q k …

Axure RP Pro 8 mac/win中文版:打造无限可能的原型设计工具

在如今的数字化时代&#xff0c;原型设计工具越来越受到设计师和产品经理们的重视。而Axure RP Pro8作为一款强大的原型设计工具&#xff0c;成为了众多专业人士的首选。 首先&#xff0c;Axure RP Pro8具备丰富的功能。它提供了多种交互元素和动画效果&#xff0c;使得用户可…

java8 LocalDate、LocalTime、LocalDateTime

LocalDate&#xff1a;表示日期&#xff08;年月日&#xff09; LocalTime &#xff1a;表示时间&#xff08;时分秒&#xff09; LocalDateTime&#xff1a;表示时间 日期 &#xff08;年月日时分秒&#xff09;&#xff0c;是java8最常用的日期类 这些类使用了final来修饰&am…