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

相关推荐
fangxiang200827 分钟前
spring boot 集成 knife4j
java·spring boot
栗豆包2 小时前
w148基于spring boot的文档管理系统的设计与实现
java·spring boot·后端·spring·tornado
Gavino.2 小时前
记录将springboot的jar包和lib分离,使用docker-compose部署
spring boot·docker·jar
念言-ny3 小时前
SpringBoot操作spark处理hdfs文件
spring boot·hdfs·spark
不怕报错 就怕不报错的小猿猿4 小时前
岚图N次方KOC项目复盘总结---记录踩坑日记
java·spring boot·maven
小盼江4 小时前
智能服装推荐系统 协同过滤余弦函数推荐服装 Springboot Vue Element-UI前后端分离
大数据·数据库·vue.js·spring boot·ui·毕业设计
CodeChampion4 小时前
69.基于SpringBoot + Vue实现的前后端分离-家乡特色推荐系统(项目 + 论文PPT)
java·vue.js·spring boot·mysql·elementui·node.js·mybatis
蔚一4 小时前
运行npm install 时,卡在sill idealTree buildDeps没有反应
前端·spring boot·后端·npm·node.js·vue
孇雙5 小时前
SpringBoot之核心配置
java·spring boot·spring
城沐小巷6 小时前
基于SpringBoot+Vue助农管理系统的设计与实现
vue.js·spring boot·助农管理系统