springcloud config配置讀取優先級


情景描述

最近在修復Eureka的靜態頁面加載不出的缺陷時,最終發現是遠程GIT倉庫將靜態資源訪問方式配置給禁用了(spring.resources.add-mappings=false)。雖然最后直接修改遠程GIT倉庫的此配置項給解決了(spring.resources.add-mappings=true),但是從中牽涉出的配置讀取優先級我們必須好好的再回顧下

springcloud config讀取倉庫配置

通過config client模塊來讀取遠程的倉庫配置,只需要在boostrap.properties文件中配置如下屬性即可

spring.application.name=eureka

spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.name=dev
spring.cloud.config.username=dev
spring.cloud.config.password=dev

其就會以GET方式去請求http://localhost:8888/eureka/dev地址從而將配置拉取下來。
當然上述的API地址也是需要被訪問服務器部署了config server服務方可調用,具體的細節就不展開了

外部源讀取優先級

我們都知道spring的配置屬性管理均是存放在Enviroment對象中,就以普通項目StandardEnvironment為例,其配置的存放順序可羅列如下

順位 key 來源 說明
1 commandLineArgs 傳入main函數的參數列表 Program arguments
2 systemProperties System.getProperties() JDK屬性列表、操作系統屬性、-D開頭的VM屬性等
3 systemEnvironment System.getEnv() 環境屬性,例如JAVA_HOME/M2_HOME
4 ${file_name} 配置文件 例如application.yml
5 defaultProperties SpringApplicationBuilder#properties()

那么遠程讀取的配置的存放應該放在上述的哪個位置呢?
我們都知道boostrap上下文通過暴露org.springframework.cloud.bootstrap.config.PropertySourceLocator接口來方便集成第三方的外部源配置讀取,比如本文提及的config client模塊中的org.springframework.cloud.config.client.ConfigServicePropertySourceLocator實現類。

但最終將外部源配置讀取以及插入至Environment對象中則是通過org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration類來完成的。

PropertySourceBootstrapConfiguration

此類也是ApplicationContextInitializer接口的實現類,閱讀過cloud源碼的都知道,此類被調用是在子類上下文初始化的時候,我們主要看下其復寫的initialize()方法

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		CompositePropertySource composite = new CompositePropertySource(
				BOOTSTRAP_PROPERTY_SOURCE_NAME);
		// 對在boostrap上下文類型為PropertySourceLocator的bean集合進行排序
		AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
		boolean empty = true;
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		for (PropertySourceLocator locator : this.propertySourceLocators) {
			PropertySource<?> source = null;
			// 讀取外部配置源
			source = locator.locate(environment);
			if (source == null) {
				continue;
			}
			logger.info("Located property source: " + source);
			composite.addPropertySource(source);
			empty = false;
		}
		if (!empty) {
			MutablePropertySources propertySources = environment.getPropertySources();
			String logConfig = environment.resolvePlaceholders("${logging.config:}");
			LogFile logFile = LogFile.get(environment);
			if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
				propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
			}
			// 插入至Environment環境對象中
			insertPropertySources(propertySources, composite);
			reinitializeLoggingSystem(environment, logConfig, logFile);
			setLogLevels(applicationContext, environment);
			handleIncludedProfiles(environment);
		}
	}

直接觀察對應的insertPropertySources()方法

	private void insertPropertySources(MutablePropertySources propertySources,
			CompositePropertySource composite) {
		// 外部源配置集合
		MutablePropertySources incoming = new MutablePropertySources();
		incoming.addFirst(composite);
		PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
		// 從外部源配置源集合中讀取PropertySourceBootstrapProperties的相關屬性
		// 例如spring.cloud.config.overrideSystemProperties等屬性
		Binder.get(environment(incoming)).bind("spring.cloud.config",
				Bindable.ofInstance(remoteProperties));
		// spring.cloud.config.allow-override=false或者spring.cloud.config.override-none=false且spring.cloud.config.override-system-properties=true
		if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone()
				&& remoteProperties.isOverrideSystemProperties())) {
			propertySources.addFirst(composite);
			return;
		}
		// spring.cloud.config.override-none=true則處於最低讀取位
		if (remoteProperties.isOverrideNone()) {
			propertySources.addLast(composite);
			return;
		}
		// 根據spring.cloud.config.override-system-properties屬性判斷是放在systemProperties前還是后
		if (propertySources
				.contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
			if (!remoteProperties.isOverrideSystemProperties()) {
				propertySources.addAfter(
						StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
						composite);
			}
			else {
				propertySources.addBefore(
						StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
						composite);
			}
		}
		else {
			propertySources.addLast(composite);
		}
	}

對上述的代碼描述作下總結
1.上述的配置屬性均會映射到PropertySourceBootstrapProperties實體類中,且其中的默認值羅列如下

屬性 默認值 說明
spring.cloud.config.allow-override true 外部源配置是否可被覆蓋
spring.cloud.config.override-none false 外部源配置是否不覆蓋任何源
spring.cloud.config.override-system-properties true 外部源配置是否可覆蓋本地屬性

2.針對相應的屬性的值對應的外部源在Environment對象中的讀取優先級,羅列如下

屬性 讀取優先級
spring.cloud.config.allow-override=false 最高
spring.cloud.config.override-none=false&&spring.cloud.config.override-system-properties=true 最高(默認)
spring.cloud.config.override-none=true 最低
spring上下文無systemEnvironment屬性 最低
spring上下文有systemEnvironment屬性 && spring.cloud.config.override-system-properties=false 在systemEnvironment之后
spring上下文有systemEnvironment屬性 && spring.cloud.config.override-system-properties=false 在systemEnvironment之前

即默認情況下,外部源的配置屬性的讀取優先級是最高的
且除了spring.cloud.config.override-none=true的情況下,其他情況下外部源的讀取優先級均比本地配置文件高
Note:值得注意的是,如果用戶想復寫上述的屬性,則放在bootstrap.yml|application.yml配置文件中是無效的,根據源碼分析只能是自定義一個PropertySourceLocator接口實現類並放置在相應的spring.factories文件中方可生效。

自定義PropertySourceLocator接口

針對上文描述,假設有這么一個場景,遠程倉庫的配置都是公有的,我們也不能修改它,我們只在項目中去復寫相應的配置以達到兼容的目的。那么用戶就需要自定義去編寫接口了
1.編寫PropertySourceLocator接口實現類

package com.example.configdemo.propertysource;

import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

import java.util.HashMap;
import java.util.Map;

/**
 * @author nanco
 * @create 19/9/22
 * @description 自定義的PropertySourceLocator的順序應該要比遠程倉庫讀取方式要優先
 * @see org.springframework.cloud.config.client.ConfigServicePropertySourceLocator
 */
@Order(value = Ordered.HIGHEST_PRECEDENCE + 1)
public class CustomPropertySourceLocator implements PropertySourceLocator {

    private static final String OVERRIDE_ADD_MAPPING = "spring.resources.add-mappings";

    @Override
    public PropertySource<?> locate(Environment environment) {

        Map<String, Object> customMap = new HashMap<>(2);
        // 遠程倉庫此配置為false,本地進行復寫
        customMap.put(OVERRIDE_ADD_MAPPING, "true");

        return new MapPropertySource("custom", customMap);
    }
}

2.編寫BootstrapConfiguration

package com.example.configdemo.propertysource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author nanco
 * @create 19/9/22
 */
@Configuration
public class CustomBootstrapConfiguration {

    @Bean("customPropertySourceLocator")
    public CustomPropertySourceLocator propertySourceLocator() {
        return new CustomPropertySourceLocator();
    }
}

3.在src\main\resources目錄下創建META-INF\spring.factories文件

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.configdemo.propertysource.CustomBootstrapConfiguration

4.運行main函數即可

小結

默認情況下,外部源配置擁有最高的優先級。在spring.cloud.config.override-none=false的情況下,外部源配置也比本地文件擁有更高的優先級。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM