需求起因
最近在实现配置中心配置热更新时,想要有效的管理配置,并且与@Value,@ConfigurationProperties等spring 特性结合起来
这让我想起了spring boot 的 Externalized Configuration(外部化配置)
知识铺垫
为了避免照本宣科官方文档之嫌。这里列举几个本人认为最重要的外部配置源,spring boot将按一定顺序管理他们。
重要的外部配置源头 | 说明 |
---|---|
JAVA 系统变量 | java -D命令后的配置 |
操作系统环境变量 | |
application.yml,application.properties文件 |
具体数据源超过10种,这里不在表述,在使用这些数据时,spring将按顺序使用他们,这就对配置数据实现缺省,覆盖需求提供了帮助
通过spring boot 的特性已知,PropertyResolver机制将直接使用spring boot外部配置源的数据完成配置的注入。
Spring Environment
所有的外部化配置,最终都被org.springframework.core.env.Environment管理
Environment的内部
environment内部管理着 org.springframework.core.env.PropertySource<T>
PropertySource以为Key-Value承载者具体的配置
所以,我们如果需要新增配置,修改配置,其实只需要操作Environment中的PropertySource即可。
配置顺序新增,删除,顺序调整
spring boot 的一个经典environment 是 org.springframework.core.env.StandardEnvironment
他的内部管理了PropertySource的集合对象org.springframework.core.env.MutablePropertySources
通过UML,可以看到关于调整配置项顺序的方法 addAfter(),addAtIndex(),addBefore(),
addFirst(),删除配置的方法 remove() 等
通过这些方法,可以方便的控制配置源的顺序,也可以将远程配置放入environment,配置中心配置刷新。
实例
public void loadRemoteConfig(){
//伪代码 仅供参考,使用时请替换变量值
//获取配置,可以通过配置中心获取
String removeConfig = ""
//转换配置数据为properties
Properties properties = convert(removeConfig);
if(properties!=null && properties.keySet().size()>0) {
//构建一个PropertySource
PropertySource p = new PropertiesPropertySource("配置源名称", properties);
StandardEnvironment environment = (StandardEnvironment) this.environment;
MutablePropertySources m = environment.getPropertySources();
//将配置插入在 XXX配置源之前
m.addBefore("XXX配置源",p);
}
}
探索,Spring Environment何时初始化?
//SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
//省略若干行
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//构建spring environment,内部派发SpringApplicationRunListener事件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//初始化spring 容器
context = createApplicationContext();
//省略若干行
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
由源码可知,spring environment初始化早于spring 容器的初始化,所以我们可以很方便的通过SpringApplicationRunListener事件来操作environment。
笔者自己就利用这个特性,在spring 容器初始化之前,加载默认配置和远程配置。