springboot Properties加载顺序源码分析
关于properties:
在spring框架中properties为Environment对象重要组成部分,
springboot有如下几种种方式注入(优先级从高到低):
1、命令行
java -jar ***.jar --spring.profiles.active=test &
2、java系统参数
System.getProperties()
3.操作系统环境变量。
环境变量。。不解释
4.从 java:comp/env 得到的 JNDI 属性。
不懂是啥,埋坑。
5.通过 RandomValuePropertySource 生成的“random.*”属性。
springboot中随机端口功能类似的东西吧。
6.应用 Jar 文件之外的属性文件(spring.config.location参数)
java -jar myproject.jar spring.config.location=classpath:/default.properties
7.应用 Jar 文件内部的属性文件
8.通过@PropertySource
@PropertySource("classpath:sys.properties")
@Configuration
public class JavaDoopConfig {
}
9.通过“SpringApplication.setDefaultProperties”声明的默认属性。
源码分析
1.SpringApplication.run方法中prepareEnvironment初始化
private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 创建Environment对象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //注入commonLine配置源,并提高有限级到最高。
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //事件监听,估计跟动态加载有关,太高端不分析了。
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        //自定义环境对象,这个也太高端,分析不了。
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        //深入jar包了。
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
1.1 getOrCreateEnvironment()方法解析:初始化StandardServletEnvironment对象。
/**
* 根据不同springboot类型创建对应的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));
        }
        //调用父类方法
        super.customizePropertySources(propertySources);
}
/**
* 调用StandardEnvironment对象初始化,系统参数
**/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
        //设置java参数属性源
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        //环境变量属性源
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
以上代码都是调用addLast方法,优先级最低。
所以优先级顺序:
1、servlet 上下文
2、servlet 配置文件
3、Jndi 属性源
4、java参数属性源。
5、环境变量属性源。
1.2:configureEnvironment方法解析:
protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
        //不懂
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
        //配置加入命令参数配置源
        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);
            }
           //添加到最高优先级
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
     }
}
以上代码都是调用addFirst方法,优先级最高。
所以优先级顺序:
1、commonLine 属性
2、servlet 上下文
3、servlet 配置文件
4、Jndi 属性源
5、java参数属性源。
6、环境变量属性源。
1.3:事件监听分析(mmp,看源码一步都不能省,鬼知道里面发生了啥)
发布ApplicationEnvironmentPreparedEvent事件被ConfigFileApplicationListener监听到。
/**
* 监听到事件后,处理方法
**/
private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        //从spring.factories下获取所有的EnvironmentPostProcessor
        // EnvironmentPostProcessor为springboot动态管理自定义配置源的接口
        //默认3个(json转换啊,系统参数重载啊,vopc云服务相关的)
        /**
        *  还有就是ConfigFileApplicationListener 自身也实现可该接口
        *  加载springboot相关配置源到容器中。
        **/
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }
/**
* RandomValuePropertySource 加入优先级
* 很重要,后面慢慢研究,今天只关注配置源
**/
public static void addToEnvironment(ConfigurableEnvironment environment) {
        //加载环境变量优先级后面
        environment.getPropertySources().addAfter(
                StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
                new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
        logger.trace("RandomValuePropertySource add to Environment");
    }
/**
* 然后调用了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);
            }
        }
变更后所以优先级顺序:
1、commonLine 属性
2、servlet 上下文
3、servlet 配置文件
4、Jndi 属性源
5、java参数属性源。
6、环境变量属性源。
7、random属性源。
8、springboot 配置源(application-dev.yml 优先于application.yml)
      
springboot Properties加载顺序源码分析的更多相关文章
- Springboot 加载配置文件源码分析
		
Springboot 加载配置文件源码分析 本文的分析是基于springboot 2.2.0.RELEASE. 本篇文章的相关源码位置:https://github.com/wbo112/blogde ...
 - Springboot学习04-默认错误页面加载机制源码分析
		
Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...
 - ElasticSearch 启动时加载 Analyzer 源码分析
		
ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档 ...
 - 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)
		
目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...
 - Spring Cloud Nacos实现动态配置加载的源码分析
		
理解了上述Environment的基本原理后,如何从远程服务器上加载配置到Spring的Environment中. NacosPropertySourceLocator 顺着前面的分析思路,我们很自然 ...
 - jQuery实现DOM加载方法源码分析
		
传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... } 但 ...
 - Spring boot加载REACTIVE源码分析
		
一,加载REACTIVE相关自动配置 spring boot通过判断含org.springframework.web.reactive.DispatcherHandler字节文件就确定程序类型是REA ...
 - spring启动component-scan类扫描加载过程---源码分析
		
http://blog.csdn.net/xieyuooo/article/details/9089441#comments
 - Spring加载流程源码分析03【refresh】
		
前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...
 
随机推荐
- Amazon MWS Scratchpad
			
https://mws.amazonservices.com/scratchpad/index.html Use this page to test Amazon MWS API request an ...
 - 【转】行内元素和inline-block产生的水平空隙bug
			
重构工程师们在设计代码时,有喜欢手动删除行内元素之间产生的额外空隙,并通过设置margin或padding来获取想要间距吗?如代码: <div class=“”><span clas ...
 - Nginx配置文件的反向代理
			
问题描述:项目需要预览pdf,前端控件支持的格式是http://192.168.0.1/pdf/a.pdf 是这样的,然后我就想给路径配个nginx反向代理就好了,但是配置的时候出问题了. 1.正确 ...
 - Mysql sqlyog 错误1045
			
首先打开运行窗口输入cmd进入DOS界面或者打开MySQL Workbench Step1: cd C:\Program Files\MySQL\MySQL Server 5.6\bin Step2: ...
 - Linux_高级用法
			
LInux如何压缩和解压文件 文件压缩与解压主要讲zip和tar 安静模式和文件夹 zip -r -q -o test.zip 需要打包文件 查看打包文件 du -h test.zip 上节学过的fi ...
 - Transformer的numpy实现
			
下面的代码自下而上的实现Transformer的相关模块功能.这份文档只实现了主要代码.由于时间关系,我无法实现所有函数.对于没有实现的函数,默认用全大写函数名指出,如SOFTMAX 由于时间限制,以 ...
 - linux c基础技巧
			
C语言:向文件末尾进行追加数据https://blog.csdn.net/qq_31243065/article/details/82354557 https://zhidao.baidu.com/q ...
 - 用pytorch1.0搭建简单的神经网络:进行多分类分析
			
用pytorch1.0搭建简单的神经网络:进行多分类分析 import torch import torch.nn.functional as F # 包含激励函数 import matplotlib ...
 - oracle 常用sql 经典sql函数使用 sql语法
			
各种树操作, 用来查询表中带有子父节点的信息 Oracle 树操作(select-start with-connect by-prior) select m.org_id from sm_organ ...
 - SQL语言(一)
			
数据定义语言:简称DDL(Data Definition Language) create database 数据库名 character set 'utf-8'; drop database 数据库 ...