spring boot 配置文件加载顺序问题

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 加载的配置中即可

相关推荐
iuyou️几秒前
Spring Boot知识点详解
java·spring boot·后端
一弓虽12 分钟前
SpringBoot 学习
java·spring boot·后端·学习
来自星星的猫教授2 小时前
spring,spring boot, spring cloud三者区别
spring boot·spring·spring cloud
乌夷3 小时前
使用spring boot vue 上传mp4转码为dash并播放
vue.js·spring boot·dash
A阳俊yi5 小时前
Spring Boot日志配置
java·spring boot·后端
苹果酱05675 小时前
2020-06-23 暑期学习日更计划(机器学习入门之路(资源汇总)+概率论)
java·vue.js·spring boot·mysql·课程设计
斜月5 小时前
一个服务预约系统该如何设计?
spring boot·后端
Java水解6 小时前
线程池详解:在SpringBoot中的最佳实践
spring boot·后端
阿里小阿希6 小时前
解决 Spring Boot + MyBatis 项目迁移到 PostgreSQL 后的数据类型不匹配问题
spring boot·postgresql·mybatis
码起来呗7 小时前
基于SpringBoot的高校学习讲座预约系统-项目分享
spring boot·后端·学习