Spring Boot 配置文件加载源码解析与升级遇到的坑

SpringBoot 配置文件加载

SpringBoot2.4以后

SpringBoot2.4及以后,取消ConfigFileApplicationListener,改成了EnvironmentPostProcessorRandomPropertySource

EnvironmentPostProcessor

EnvironmentPostProcessor子类实现的加载本质上SpringFactoriesLoader进行类的加载,加载时机是通过ApplicationEnvironmentPreparedEvent事件监听来实现。 SpringBoot2.4以前是ConfigFileApplicationListener,SpringBoot2.4+是通过EnvironmentPostProcessorApplicationListener,SpringBoot2.4版本对外部配置进行大幅度调整,由于ConfigFileApplicationListener过于笨重职责过多替换成EnvironmentPostProcessorApplicationListener、ConfigDataEnvironmentPostProcessor来实现。

具体可以查看官方说明:

Provide ConfigFileApplicationListener replacement · spring-projects/spring-boot@3352024 (github.com)

EnvironmentPostProcessor提供了一个接入点,允许用户在Spring上下文构建之前,设置一些环境信息Environment。

官方文档:Customize the Environment or ApplicationContext Before It Starts

其中一种配置方式是starter机制一样,是在META-INF/spring.factories文件里配置的

ConfigDataEnvironment

在这个文件里配置了配置文件路径等信息

ConfigDataEnvironmentPostProcessor

优先级总结

  1. 后缀优先级:

yml>yaml>properties

  1. 以下优先级从低到高,每行也是从低到高

1. `optional:classpath:/;optional:classpath:/config/`
2. `optional:file:./;optional:file:./config/;optional:file:./config/*/`
3. `optional:classpath:custom-config/`
4. `optional:file:./custom-config/`

SpringBoot启动发布ApplicationEnvironmentPreparedEvent

org.springframework.boot.SpringApplication.prepareEnvironment方法

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        //发布ApplicationEnvironmentPreparedEvent事件
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = convertEnvironment(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

EnvironmentPostProcessorApplicationListener收到ApplicationEnvironmentPreparedEvent事件并处理EnvironmentPostProcessor子类

    //事件回调处理
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        //判断事件类型
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent();
        }
        if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }
​
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        //获取EnvironmentPostProcessors实现子类并调用postProcessEnvironment方法
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }

SpringBoot2.4以前

ConfigFileApplicationListener

ConfigFileApplicationListener类的属性如下,根据属性可以大致看出支持的配置和功能:

  • 默认配置文件的路径:DEFAULT_SEARCH_LOCATIONS,注意具体处理的时候进行了反转,优先级如下:

    从高到低

    1. file:./custom-config/

    2. classpath:custom-config/

    3. file:./config/

    4. file:./

    5. classpath:/config/

    6. classpath:/

  • 支持定义配置文件:配置spring.profiles.active对应的值,如dev。application-dev.properties

  • 支持定义使用的配置文件名:spring.config.name

  • 支持定义使用的配置文件所在路径:spring.config.location。需要在环境变量里配置

  • 支持定义使用的配置文件所在路径:spring.config.additional-location。需要在环境变量里配置

  • 等等

public class ConfigFileApplicationListener
      implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
​
   private static final String DEFAULT_PROPERTIES = "defaultProperties";
​
   // Note the order is from least to most specific (last one wins)
   private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
​
   private static final String DEFAULT_NAMES = "application";
​
   private static final Set<String> NO_SEARCH_NAMES = Collections.singleton(null);
​
   /**
    * The "active profiles" property name.
    */
   public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
​
   /**
    * The "includes profiles" property name.
    */
   public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
​
   /**
    * The "config name" property name.
    */
   public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
​
   /**
    * The "config location" property name.
    */
   public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
​
   /**
    * The "config additional location" property name.
    */
   public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
​
   /**
    * The default order for the processor.
    */
   public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
​
}

onApplicationEnvironmentPreparedEvent

在ApplicationEnvironmentPreparedEvent事件的处理。主要是将加载后置处理器

private void onApplicationEnvironmentPreparedEvent(
      ApplicationEnvironmentPreparedEvent event) {
    // 加载在META-INF/spring.factories里定义的EnvironmentPostProcessor对应的类名
   List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    // 将自己也加入其中
   postProcessors.add(this);
    // 根据order排序
   AnnotationAwareOrderComparator.sort(postProcessors);
    // 遍历执行
   for (EnvironmentPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessEnvironment(event.getEnvironment(),
            event.getSpringApplication());
   }
}

postProcessors:

postProcessors = {ArrayList@2862}  size = 5
 0 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor@2885} 
 1 = {SpringApplicationJsonEnvironmentPostProcessor@2884} 
 2 = {CloudFoundryVcapEnvironmentPostProcessor@2883} 
 3 = {HostInfoEnvironmentPostProcessor@2886} 
 4 = {ConfigFileApplicationListener@2741} 

ConfigFileApplicationListener的加载自定义属性开始

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
          SpringApplication application) {
       addPropertySources(environment, application.getResourceLoader());
    }
    /**
     * Add config file property sources to the specified environment.
     * @param environment the environment to add source to
     * @param resourceLoader the resource loader
     * @see #addPostProcessors(ConfigurableApplicationContext)
     */
    protected void addPropertySources(ConfigurableEnvironment environment,
                                      ResourceLoader resourceLoader) {
        // 自定义属性的加载
        RandomValuePropertySource.addToEnvironment(environment);
        // 开始加载操作
        new Loader(environment, resourceLoader).load();
    }

对比

SpringBoot2.4及以后,和SpringBoot2.4之前相比,多了读取config文件夹下的子文件夹里的配置文件的功能!如读取:file:./config/mysql/application.properties

容易踩坑:SpringBoot2.4及以后,将备份的配置文件保存到config/backup/文件夹里,会导致备份的配置文件属性也被读取

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值