已知:spring cloud先加载bootstrap,后加载远程config-server的application。
对于一个服务,需要公共配置:
- 注册中心地址;
- 日志级别
- configServer的server-id,profile,label
另外需要私有配置:
- spring.application.name服务名
- server.port端口名
- 需要在application拉取的配置名。
问题:目前所有服务都需要一个bootstrap.yml,并且不能公用。怎么才能简化本地配置?
解决方案:
- 想办法将bootstrap一分为二,加载一个服务器上共有的配置,并加载一个私有配置(或者直接以环境变量形式写在启动脚本中。)
- 在main方法之前,自己加载这两个配置,并存储到环境变量中。
- 自己加载公共配置文件,私有配置放入环境变量;并将自己加载的配置文件注入springboot
考虑方法1,先看源码。
入口:SpringApplication.run(String... args)
,,,,,,
另一个切入点:ConfigFileApplicationListener.onApplicationEvent
,,,,,,
可以尝试加入spring.config.location环境变量来指定加载配置文件。
考虑到执行SpringApplication.run方法之前可能还需要做其他事情。所以这个干脆也放在run方法外面了。采用方法2。
执行方法2时发现一个问题。可能springboot并不会从系统环境变量中读取相应配置,并且将所有服务配置都放到环境变量中,有点污染环境变量的感觉。于是想到方法3
读取源码发现,spring有一个ConfigurableEnvironment,其中有一个propertySources属性,维护了一个配置list。关键在于想办法把配置加载到这个list中
本地配置文件-bootstrap.yml加载时间点为:ConfigFileApplicationListener.onApplicationEvent。在spring触发这个事件的时候会加载相应配置文件。
代码如下。
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
在onApplicationEnvironmentPreparedEvent方法中,调用了postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());方法。
两个方案:1. 自己写一个listener,监听onApplicationEvent。2.实现EnvironmentPostProcessor接口,等着spring调用
使用方案2。
定义:public class CommonConfig implements EnvironmentPostProcessor
初始化配置方法:
private static Properties prop = new Properties();
public static void init(String[] args) {
String filePathStr = sysProp.getProperty(sysProp_filePath, defaultConfigFilePath);
if (!StringUtils.isEmpty(filePathStr)) {
// 加载配置文件
String[] filePaths = filePathStr.split(";");
for (String filePath : filePaths) {
Properties properties = resolveFileAddProp(filePath, prop);
prop.putAll(properties);
}
}
}
实现接口方法:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try {
init(null);
PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("blogCommon", prop);
environment.getPropertySources().addLast(propertiesPropertySource);
LOG.info("CommonConfig...postProcessEnvironment end!配置文件加载完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
然后在META-INF/spring.factories中添加:
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
com.xs.blog.env.CommonConfig
大功告成。启动之后会出现这一行日志。“CommonConfig...postProcessEnvironment end!配置文件加载完毕”