zoukankan      html  css  js  c++  java
  • 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)

     




  • 相关阅读:
    boost.property_tree的高级用法(你们没见过的操作)
    MFC- OnIdle空闲处理
    华为代码质量军规 (1) 数组访问,必须进行越界保护
    WinSocket 编程
    【C/C++】链表的理解与使用
    单链表
    C++ lambda表达式 (二)
    C++ lambda表达式 (一)
    C++11 volatile 类型
    关于结构体内存对齐方式的总结(#pragma pack()和alignas())
  • 原文地址:https://www.cnblogs.com/exmyth/p/11349575.html
Copyright © 2011-2022 走看看