springboot配置文件加载顺序和机制
前言
springboot提供强大的,多样的配置加载,你知道他的加载机制嘛?他是怎么运作的?我们怎么自定义扩展呢?今天就来扒一扒spring的配置文件加载顺序和机制。只有了解了,我们才能在多配置中找到正确的配置,处理问题
测试
测试1
当只有一个配置文件时 resources/application.yml
并配置:
client:
name: 张三
输出如下:
测试2
当存在两个配置文件 resources/application.yml 配置1
和 resources/application.properties 配置2
。resources/application.yml
中的配置不变,resources/application.properties
如下:
client.name = 李四
输出如下:
结论:可以看到 配置2 和 配置1 同时存在时 , application.properties
中的配置将会覆盖 application.yml
中的配置属性。
测试3
当存在两个配置文件 resources/application.yml 配置1
和 resources/config/application.yml 配置2
输出如下:
结论:可以看到 配置2 中的配置会覆盖 配置1 中的属性。
测试4
在项目根路径添加配置文件。这种在idea中测试需要创建文件,然后拷贝target目录中,然后命令启动项目测试: java -jar boot-client-0.0.1-SNAPSHOT.jar
。
结论:/config/application.yml > /application.yml
。
结论
- 项目根路径上(projectPath)的配置文件优先级高于类路径下的配置(resources)文件
- 同级别目录下 config下的配置高于 当前跟路径下的配置。比如
resources/application.properties
中的配置优先级高于resources/application.yml
。 - 同级别目录下
application.properties
配置优先级高于application.yml
。 - 经过测试,在使用 java -jar 启动项目时, spring.config.location 指定的配置文件优先级最高。 比如:
java -jar boot-client-0.0.1-SNAPSHOT.jar --spring.config.name=applicationtest.yml --spring.config.location=file:/Users/zhaolangjing/code/github/poe4j/boot-client/target/applicationName.yml
- 配置文件加载获取属性配置优先级:
spring.config.location >
${project.path}/config/application.properties >
${project.path}/config/application.yml >
${project.path}/application.properties >
${project.path}/application.yml >
${classpath}/config/application.properties >
${classpath}/config/application.yml >
${classpath}/application.properties >
${classpath}/application.yml >
原理解析
StandardServletEnvironment
我们在 StandardServletEnvironment 类中的 initPropertySources() 方法上打上断点,当
initPropertySources方法执行结束完成之后,我们可以看到属性: List<PropertySource<?>> propertySourceList
列表中属性文件的排序。如下图:
从图中我们可以看出,spring给对象属性赋值的逻辑,给配置文件拍好顺序后,采用最前匹配原则赋值。
ConfigDataEnvironmentPostProcessor
ConfigDataEnvironmentPostProcessor就是用来解析配置文件的后置处理器。大家可以自行去阅读源码理解其加载配置文件的过程。
EnvironmentPostProcessor
springboot环境后置处理器接口。 那么假如我们想自定义一些环境配置,就可以实现该接口,比如我们远程加载一些配置文件,比如从SVN,git等等。
- 允许在刷新应用程序上下文之前自定义应用程序。
- Environment环境后处理器实现必须在 中 META-INF/spring.factories注册,使用此类的完全限定名作为键。
- 实现 Ordered 希望按特定顺序调用,可以实现接口或使用 @Order 注释。
自定义实现环境后置处理器
我们自定义实现一个后置处理器,用于替换我们前面前面示例代码中的 client.name
的值。要实现这个功能要实现以下功能:
- 实现 EnvironmentPostProcessor 接口并实现 postProcessEnvironment 方法。
-
- 添加配置内容(可以使用 Properties 也可以用 Map)
client.name = "奥特曼"
- 将配置内容按照规定格式放入容器列表中
- 放入时要注意放入的位置,我们应该放在默认加载的配置文件最前面的位置。
- 添加配置内容(可以使用 Properties 也可以用 Map)
- 添加 META-INF/spring.factories , 并写入我们自己的后置处理器,让spring容器加载该bean。
代码如下:
第一步,添加META-INF/spring.factories 并写入自己的处理器类
org.springframework.boot.env.EnvironmentPostProcessor=com.poe4j.boot.client.MyEnvironmentPostProcessor
第二步 MyEnvironmentPostProcessor 实现EnvironmentPostProcessor。
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 1
Properties myProperties = new Properties();
myProperties.setProperty("client.name", "奥特曼");
myProperties.setProperty("client.path", MyEnvironmentPostProcessor.class.getName());
// 2
PropertiesPropertySource PropertiesPropertySource = new PropertiesPropertySource(
"myClient", myProperties);
// 3
MutablePropertySources mutablePropertySources = environment.getPropertySources();
Optional<PropertySource<?>> first = mutablePropertySources.stream().filter(v -> v.getName().contains("application")).findFirst();
if (first.isPresent()) {
mutablePropertySources.addBefore(first.get().getName(), propertySources);
} else {
mutablePropertySources.addLast(propertySources);
}
}
1 使用Properties 添加配置,设置值:client.name = “奥特曼”。
2 将我们添加的配置内容实例化到 PropertiesPropertySource 。
3 将 PropertiesPropertySource 装到 mutablePropertySources中。这一步要注意放入的位置。比如我这里演示的是先通过 mutablePropertySources 迭代拿到 已有的通过配置文件加载的 PropertySource 。 如果找到了,那么就将我们的配置文件放到该文件的前面。如果没找到就放到 mutablePropertySources 的最后。ps:mutablePropertySources.stream().filter(v -> v.getName().contains("application")).findFirst() 这段代码的查找逻辑并不是很严谨,这里只是为了演示方便。请大家自行修改。
我们查看一个实际结果:
可以看到我们的自定义处理器的配置确实生效了。
附录
问题1:
在遇到application.properties文件中文乱码时(我使用的idea),需要在idea的setting中修改 Editor -> File Encodings ,做出如下修改:
将Properties Files (*.properties)下的Default encoding for properties files设置为UTF-8,将Transparent native-to-ascii conversion前的勾选上,一定要吧Transparent native-to-ascii conversion勾选上,不然不生效。
转载自:https://juejin.cn/post/7223358128329785405