spring boot 配置文件加载顺序问题
基于spring boot 2.7.x
一般场景
文件加载顺序bootstrap.properties > bootstrap.yml > application.properties > application.yml
,而配置生效的顺序application>bootstrap
, 即文件加载顺序优先级越低,则配置生效优先级越高。【注意, application-dev.properties > application.properties 配置顺序】当需要覆盖默认配置时,只需要在 application配置文件上覆盖就行
特殊场景
在自定义了 PropertySourceLocator 后
, 比较常见的是 使用配置中心,无论是 Nacos还是 使用 spring cloud config
配置中心,这时候就要注意了,单纯的在 本地的 application.properties下覆盖有可能是不生效的
首先看下是如何加载的,看源码 PropertySourceBootstrapConfiguration
PropertySourceBootstrapConfiguration
- 通过 autowired 注入 PropertySourceLocator 实现类
- 分别在 ContextRefreshedEvent 事件 和 初始化时进行 PropertySourceLocator 拉取自定义配置
java
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration implements ApplicationListener<ContextRefreshedEvent>,
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
/**
* Bootstrap property source name.
*/
public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME
+ "Properties";
private static Log logger = LogFactory.getLog(PropertySourceBootstrapConfiguration.class);
private int order = Ordered.HIGHEST_PRECEDENCE + 10;
@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
@Autowired
private PropertySourceBootstrapProperties bootstrapProperties;
@Override
public int getOrder() {
return this.order;
}
public void setPropertySourceLocators(Collection<PropertySourceLocator> propertySourceLocators) {
this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
if (!bootstrapProperties.isInitializeOnContextRefresh() || !applicationContext.getEnvironment()
.getPropertySources().contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
doInitialize(applicationContext);
}
}
private void doInitialize(ConfigurableApplicationContext applicationContext) {
List<PropertySource<?>> composite = new ArrayList<>();
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
// 遍历 自定义 locator 加载配置
for (PropertySourceLocator locator : this.propertySourceLocators) {
Collection<PropertySource<?>> source = locator.locateCollection(environment);
if (source == null || source.size() == 0) {
continue;
}
List<PropertySource<?>> sourceList = new ArrayList<>();
for (PropertySource<?> p : source) {
if (p instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;
sourceList.add(new BootstrapPropertySource<>(enumerable));
}
else {
sourceList.add(new SimpleBootstrapPropertySource(p));
}
}
logger.info("Located property source: " + sourceList);
//将自定义的 PropertySource 放入 composite 列表
composite.addAll(sourceList);
empty = false;
}
if (!empty) {
// propertySources 是已经排好序的,即取配置的顺序: application > bootstrap
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
for (PropertySource<?> p : environment.getPropertySources()) {
if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
propertySources.remove(p.getName());
}
}
// 这个地方就要根据条件将自定义propertySource 有序放入 MutablePropertySources
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(applicationContext, environment);
handleProfiles(environment);
}
}
private void insertPropertySources(MutablePropertySources propertySources, List<PropertySource<?>> composite) {
MutablePropertySources incoming = new MutablePropertySources();
List<PropertySource<?>> reversedComposite = new ArrayList<>(composite);
// Reverse the list so that when we call addFirst below we are maintaining the
// same order of PropertySources
// Wherever we call addLast we can use the order in the List since the first item
// will end up before the rest
//先倒序
Collections.reverse(reversedComposite);
for (PropertySource<?> p : reversedComposite) {
incoming.addFirst(p);
}
PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
Binder.get(environment(incoming)).bind("spring.cloud.config", Bindable.ofInstance(remoteProperties));
if (!remoteProperties.isAllowOverride()
|| (!remoteProperties.isOverrideNone() && remoteProperties.isOverrideSystemProperties())) {
//1.如果远程配置 allowOverride=false 或者
//2 overrideNone=false && overrideSystemProperties=true
//则将序号越小的propertySource放在MutablePropertySources中
//即优先取自定义PropertySourceLocator的配置,然后再本地配置
//1>2>本地配置
for (PropertySource<?> p : reversedComposite) {
if (propertySources.contains(DECRYPTED_PROPERTY_SOURCE_NAME)) {
propertySources.addAfter(DECRYPTED_PROPERTY_SOURCE_NAME, p);
}
else {
propertySources.addFirst(p);
}
}
return;
}
// 如果:overrideNone=true,即优先取本地配置,然后再自定义PropertySourceLocator的配置
//本地配置> 1>2
if (remoteProperties.isOverrideNone()) {
for (PropertySource<?> p : composite) {
//将自定义配置放在最后
propertySources.addLast(p);
}
return;
}
if (propertySources.contains(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
if (!remoteProperties.isOverrideSystemProperties()) {
for (PropertySource<?> p : reversedComposite) {
propertySources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, p);
}
}
else {
for (PropertySource<?> p : composite) {
propertySources.addBefore(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, p);
}
}
}
else {
for (PropertySource<?> p : composite) {
propertySources.addLast(p);
}
}
}
/*
* The ConextRefreshedEvent gets called at the end of the boostrap phase after config
* data is loaded during bootstrap. This will run and do an "initial fetch" of
* configuration data during bootstrap but before the main applicaiton context starts.
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (bootstrapProperties.isInitializeOnContextRefresh()
&& event.getApplicationContext() instanceof ConfigurableApplicationContext) {
if (((ConfigurableApplicationContext) event.getApplicationContext()).getEnvironment().getPropertySources()
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
doInitialize((ConfigurableApplicationContext) event.getApplicationContext());
}
}
}
}
分析完,总结一下,在使用自定义 PropertySourceLocator 场景下
- 如果远程配置 allowOverride=false 或者 2 overrideNone=false && overrideSystemProperties=true
- PropertySourceLocator_1 > PropertySourceLocator_2 > 本地配置(application.properties)
- 如果:overrideNone=true
- 本地配置(application.properties) > PropertySourceLocator_1 > PropertySourceLocator_2
这里还有一个问题 remoteProperties 注入的问题,如果想覆盖远程使用本地配置,你是这样在本地application.properties 或者 bootstrap.properties 配置的:
properties
spring.cloud.config.allow-override=true
spring.cloud.config.override-none=true
spring.cloud.config.override-system-properties=false
发现还不生效,这是因为 remoteProperties 是通过注入 自定义PropertySourceLocator 的配置,而非本地application.properties 或者 bootstrap.properties
所以只要将这些配置放在远程配置中心,或者其他自定义 PropertySourceLocator 加载的配置中即可