springboot Properties加載順序源碼分析

關于properties:

在spring框架中properties為Environment對象重要組成部分,

springboot有如下幾種種方式注入(優(yōu)先級從高到低):

1、命令行
java -jar ***.jar --spring.profiles.active=test &
2、java系統(tǒng)參數(shù)
System.getProperties()
3.操作系統(tǒng)環(huán)境變量。
環(huán)境變量。。不解釋
4.從 java:comp/env 得到的 JNDI 屬性。
不懂是啥,埋坑。
5.通過 RandomValuePropertySource 生成的“random.*”屬性。
springboot中隨機端口功能類似的東西吧。
6.應用 Jar 文件之外的屬性文件(spring.config.location參數(shù))
java -jar myproject.jar spring.config.location=classpath:/default.properties
7.應用 Jar 文件內(nèi)部的屬性文件
8.通過@PropertySource
@PropertySource("classpath:sys.properties")
@Configuration
public class JavaDoopConfig {
 
}
9.通過“SpringApplication.setDefaultProperties”聲明的默認屬性。

源碼分析

1.SpringApplication.run方法中prepareEnvironment初始化

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 創(chuàng)建Environment對象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //注入commonLine配置源,并提高有限級到最高。
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //事件監(jiān)聽,估計跟動態(tài)加載有關,太高端不分析了。
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        //自定義環(huán)境對象,這個也太高端,分析不了。
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        //深入jar包了。
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
1.1 getOrCreateEnvironment()方法解析:初始化StandardServletEnvironment對象。
/**
* 根據(jù)不同springboot類型創(chuàng)建對應的Environment對象
**/
private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
/**
* 初始化StandardServletEnvironments時,默認添加幾種propertySources。
**/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
        //servlet上下文中的屬性源
        propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
        //servlet配置文件中的屬性源  
        propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
        //Jndi中配置的屬性源
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
        }
        //調(diào)用父類方法
        super.customizePropertySources(propertySources);
}

/**
* 調(diào)用StandardEnvironment對象初始化,系統(tǒng)參數(shù)
**/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
        //設置java參數(shù)屬性源
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        //環(huán)境變量屬性源
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

以上代碼都是調(diào)用addLast方法,優(yōu)先級最低。
所以優(yōu)先級順序:
1、servlet 上下文
2、servlet 配置文件
3、Jndi 屬性源
4、java參數(shù)屬性源。
5、環(huán)境變量屬性源。

1.2:configureEnvironment方法解析:
protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
        //不懂
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
        //配置加入命令參數(shù)配置源
        configurePropertySources(environment, args);
        configureProfiles(environment, args);
}

protected void configurePropertySources(ConfigurableEnvironment environment,String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            sources.addLast(
                    new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource(
                        "springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
           //添加到最高優(yōu)先級
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
     }
}

以上代碼都是調(diào)用addFirst方法,優(yōu)先級最高。
所以優(yōu)先級順序:
1、commonLine 屬性
2、servlet 上下文
3、servlet 配置文件
4、Jndi 屬性源
5、java參數(shù)屬性源。
6、環(huán)境變量屬性源。

1.3:事件監(jiān)聽分析(mmp,看源碼一步都不能省,鬼知道里面發(fā)生了啥)
發(fā)布ApplicationEnvironmentPreparedEvent事件被ConfigFileApplicationListener監(jiān)聽到。

/**
* 監(jiān)聽到事件后,處理方法
**/
private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        //從spring.factories下獲取所有的EnvironmentPostProcessor
        // EnvironmentPostProcessor為springboot動態(tài)管理自定義配置源的接口
        //默認3個(json轉(zhuǎn)換啊,系統(tǒng)參數(shù)重載啊,vopc云服務相關的)
        /**
        *  還有就是ConfigFileApplicationListener 自身也實現(xiàn)可該接口
        *  加載springboot相關配置源到容器中。
        **/
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }

/**
* RandomValuePropertySource 加入優(yōu)先級
* 很重要,后面慢慢研究,今天只關注配置源
**/
public static void addToEnvironment(ConfigurableEnvironment environment) {
        //加載環(huán)境變量優(yōu)先級后面
        environment.getPropertySources().addAfter(
                StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
                new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
        logger.trace("RandomValuePropertySource add to Environment");
    }
/**
* 然后調(diào)用了springboot讀取配置的核心方法。
**/
public void load() {
            this.profiles = new LinkedList<>();
            this.processedProfiles = new LinkedList<>();
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            initializeProfiles();
            while (!this.profiles.isEmpty()) {
                Profile profile = this.profiles.poll();
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                this.processedProfiles.add(profile);
            }
            resetEnvironmentProfiles(this.processedProfiles);
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));
            //上面各種嵌套邏輯,加載好配置后
            addLoadedPropertySources();
        }

/**
* 依次加載配置文件到最后。
**/
private void addLoadedPropertySource(MutablePropertySources destination,
                String lastAdded, PropertySource<?> source) {
            if (lastAdded == null) {
                if (destination.contains(DEFAULT_PROPERTIES)) {
                    destination.addBefore(DEFAULT_PROPERTIES, source);
                }
                else {
                    destination.addLast(source);
                }
            }
            else {
                destination.addAfter(lastAdded, source);
            }
        }

變更后所以優(yōu)先級順序:
1、commonLine 屬性
2、servlet 上下文
3、servlet 配置文件
4、Jndi 屬性源
5、java參數(shù)屬性源。
6、環(huán)境變量屬性源。
7、random屬性源。
8、springboot 配置源(application-dev.yml 優(yōu)先于application.yml)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容