Spring Boot 启动(四) EnvironmentPostProcessor
Spring Boot 启动(四) EnvironmentPostProcessor
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
- Spring Boot 配置使用
- Spring Boot 配置文件加载流程分析 - ConfigFileApplicationListener
- Spring Boot 配置文件加载 - EnvironmentPostProcessor
一、EnvironmentPostProcessor
ConfigFileApplicationListener 是 Spring Boot 中处理配置文件的监听器。
// ConfigFileApplicationListener
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
// 1. 委托给 EnvironmentPostProcessor 处理,也是通过 spring.factories 配置
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// 2. ConfigFileApplicationListener 本身也实现了 EnvironmentPostProcessor 接口
postProcessors.add(this);
// 3. spring 都都通过 AnnotationAwareOrderComparator 控制执行顺序
AnnotationAwareOrderComparator.sort(postProcessors);
// 4. 执行 EnvironmentPostProcessor
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
在 spring.factories 配置文件中默认定义了三个 EnvironmentPostProcessor 的实现类:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
优先级 SystemEnvironmentPropertySourceEnvironmentPostProcessor > SpringApplicationJsonEnvironmentPostProcessor
SystemEnvironmentPropertySourceEnvironmentPostProcessor对 systemEnvironment 属性进行了包装。SpringApplicationJsonEnvironmentPostProcessor解析 spring.application.json 或 SPRING_APPLICATION_JSON 配置的 json 字符串。
本节专门介绍一下 SystemEnvironmentPropertySourceEnvironmentPostProcessor 如何解析 JSON 格式的。
二、spring.application.json 使用
spring.application.json 或 SPRING_APPLICATION_JSON 定义的 json 字符串。命令行配置如下:
java -jar xxx.jar --spring.application.json='{"foo":"bar"}'
编程方式如下:
@Test
public void list() {
assertThat(this.environment.resolvePlaceholders("${foo[1]:}")).isEmpty();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment,
"SPRING_APPLICATION_JSON={\"foo\":[\"bar\",\"spam\"]}");
this.processor.postProcessEnvironment(this.environment, null);
assertThat(this.environment.resolvePlaceholders("${foo[1]:}")).isEqualTo("spam");
}
三、源码分析
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
// environment 中定义的 spring.application.json 或 SPRING_APPLICATION_JSON 解析成 JsonPropertySource
// 默认只会解析第一个配置的 json 字符串
propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull)
.findFirst().ifPresent((v) -> processJson(environment, v));
}
private void processJson(ConfigurableEnvironment environment,
JsonPropertyValue propertyValue) {
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(propertyValue.getJson());
if (!map.isEmpty()) {
addJsonPropertySource(environment,
new JsonPropertySource(propertyValue, flatten(map)));
}
}
这里我们还需要注意 JsonPropertySource 的读取顺序。spring.application.json 的级别非常高,只低于命令行配置。
private void addJsonPropertySource(ConfigurableEnvironment environment,
PropertySource<?> source) {
MutablePropertySources sources = environment.getPropertySources();
String name = findPropertySource(sources);
if (sources.contains(name)) {
sources.addBefore(name, source);
} else {
sources.addFirst(source);
}
}
// 默认会放到 jndiProperties 或 systemProperties 之前
private String findPropertySource(MutablePropertySources sources) {
if (ClassUtils.isPresent(SERVLET_ENVIRONMENT_CLASS, null) && sources
.contains(StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME)) {
return StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME;
}
return StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME;
}
3.1 map 解析成 properties 分析
这里可以看到 Spring 将 map 转为 properties 的代码十分简洁。
// "SPRING_APPLICATION_JSON={\"foo\":[\"bar\",\"spam\"]}" -> ${foo[1]:}=spam
// "SPRING_APPLICATION_JSON={\"foo.bar\":\"spam\"}" -> ${foo.bar:}=spam
// "SPRING_APPLICATION_JSON={\"foo\":{\"bar\":\"spam\",\"rab\":\"maps\"}}" -> ${foo.bar:}=spam
private Map<String, Object> flatten(Map<String, Object> map) {
Map<String, Object> result = new LinkedHashMap<>();
flatten(null, result, map);
return result;
}
private void flatten(String prefix, Map<String, Object> result,
Map<String, Object> map) {
String namePrefix = (prefix != null) ? prefix + "." : "";
map.forEach((key, value) -> extract(namePrefix + key, result, value));
}
private void extract(String name, Map<String, Object> result, Object value) {
if (value instanceof Map) {
flatten(name, result, (Map<String, Object>) value);
} else if (value instanceof Collection) {
int index = 0;
for (Object object : (Collection<Object>) value) {
extract(name + "[" + index + "]", result, object);
index++;
}
} else {
result.put(name, value);
}
}
每天用心记录一点点。内容也许不重要,但习惯很重要!
Spring Boot 启动(四) EnvironmentPostProcessor的更多相关文章
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot 启动(二) Environment 加载
Spring Boot 启动(二) Environment 加载 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 上一节中 ...
- Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- spring boot / cloud (四) 自定义线程池以及异步处理@Async
spring boot / cloud (四) 自定义线程池以及异步处理@Async 前言 什么是线程池? 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线 ...
- Spring Boot 启动(二) 配置详解
Spring Boot 启动(二) 配置详解 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Boot 配置 ...
- Spring Boot 启动(一) SpringApplication 分析
Spring Boot 启动(一) SpringApplication 分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html ...
- Spring Boot 2 (四):使用 Docker 部署 Spring Boot
Spring Boot 2 (四):使用 Docker 部署 Spring Boot Docker 技术发展为微服务落地提供了更加便利的环境,使用 Docker 部署 Spring Boot 其实非常 ...
- Spring Boot(十四):spring boot整合shiro-登录认证和权限管理
Spring Boot(十四):spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉 ...
随机推荐
- 【C语言基础】什么是数据类型?
基本数据类型 整数 整型 — — int ---- 4 字节 短整型 — — short int ---- 2 字节 长整型 — — long int ---- 8 字节 ...
- 关于vue
render:(h)=>h(App):声明App组件的内容 挂载到节点 const root=document.createElement("div")document.bo ...
- c#关于var的介绍和用法
var关键字---根据初始化语句推断变量类型 功能: var关键字指示编译器根据初始化语句右侧的表达式推断变量的类型,推断类型可以是内置类型,匿名类型,用户定义类型,.NET Framework类库中 ...
- 我与OO (1)
前言 “真正的勇士敢于面对惨淡的人生,敢于面对淋漓的鲜血” 我是谨慎拜读了鲁迅先生的名言,怀着崇敬的精神去接触这门课程的. 而当我真的经历了这门课程以后,我才发现,刘和珍君这样的觉悟,我们普通人,果然 ...
- 使用 nodeJs 开发微信公众号(配置服务器)
流程如下: 1. 申请微信公众号:企业号.服务号.订阅号(前两个要钱) 2. 配置微信公众号后台 选择基本配置,获得 AppId 和 AppSecret ,点击服务器配置 URL:你服务器地址,不能是 ...
- if判断,switch语句
if ; else if; else; 判断操作: 格式示例: public class scanner { public static void main(String[] args){ int a ...
- HASHSET不能预留容量问题
如果,它没有像 var lst = new List<int>(32) 这样可以直接预留容量的操作,原因未知. 但可以通过如下操作进行预留: //给hashset预留容量,防止动态扩容,由 ...
- Springboot 使用thymeleaf模板layout布局
使用layout布局前应该在pom文件中导入thymeleaf(dialect)依赖:如下 <properties> <project.build.sourceEncoding> ...
- 微信小程序--录音
var app = getApp(), $ = require("../../utils/util.js"); const recorderManager = wx.getReco ...
- 对于在git上面拉代码报"error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054"解决方法
主要原因是安全设置的问题: 首先执行git config http.sslVerify "false" 若出现下列错误 git config http.sslVerify &q ...