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

相关推荐
用户83071968408210 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解11 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解11 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记15 小时前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者1 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840821 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解1 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者2 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺2 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart2 天前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot