Spring Boot源码分析-配置文件加载原理

本文深入探讨Spring Boot的配置文件加载原理,从何时开始加载、如何读取配置内容,以及如何处理不同环境的配置。通过源码分析,揭示了在启动过程中配置文件的加载时机,以及在读取配置文件时如何处理中文乱码问题和环境配置的优先级确定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读。今天让我们继续阅读源码,了解配置文件加载原理。

基于Spring Boot 2.1.0.RELEASE

在开始阅读源码之前,首先准备三个问题。

  1. 什么时候开始加载配置文件?
  2. 如何读取相关配置文件内容?
  3. 如何区分不同环境的配置?

下面用Spring代替Spring Boot

接下来进入主题,首先关注第一个问题。

一、什么时候开始加载配置文件?

Spring Boot源码分析-启动过程中我们可以得知,Spring在启动的过程中发布了ApplicationEnvironmentPreparedEvent事件,ConfigFileApplicationListener监听到这个消息的时候,开始实例化并调用(META-INF/spring.factories中定义)EnvironmentPostProcessorpostProcessEnvironment方法。而ConfigFileApplicationListener本身也实现了EnvironmentPostProcessor接口,且将自身加入到EnvironmentPostProcessor集合中,故也会调用自身的方法。

跟踪ConfigFileApplicationListenerpostProcessEnvironment方法源码

public void postProcessEnvironmen(ConfigurableEnvironment environment,
        SpringApplication application) {
    addPropertySources(environment,application.getResourceLoader());
}

继续跟踪addPropertySources方法

/**
 * 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(ConfigurableEnvironmentenvironment,
        ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironmen(environment);
    new Loader(environment, resourceLoader).load();
}

从注释中我们可以看出,这个方法是将配置文件内容添加到指定的Environment中。到此为止,我们已经明白了Spring是在发布ApplicationEnvironmentPreparedEvent事件之后,才开始加载配置文件的。接下来开始关注第二个问题。

二、如何读取相关配置文件内容?

继续跟踪Loader源码,LoaderConfigFileApplicationListener的一个内部类,用来读取配置文件并配置相关环境。

首先跟踪Loader构造方法(注意load存在多个方法重载)

Loader(ConfigurableEnvironment environmentResourceLoader resourceLoader) {
    this.environment = environment;
    this.placeholdersResolver = nePropertySourcesPlaceholdersResolver(
            this.environment);
    this.resourceLoader = (resourceLoader != null) resourceLoader
            : new DefaultResourceLoader();
    // 实例化配置文件读取工具
    this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
            PropertySourceLoader.class, getClass.getClassLoader());
}

SpringFactoriesLoader.loadFactories获取META-INF/spring.factories中预定义的类

org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

从类名中可以看出这两个类主要是用来读取.properties.yml文件

继续跟踪load方法

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,
                addToLoad(MutablePropertySources::addLastfalse));
        this.processedProfiles.add(profile);
    }
    resetEnvironmentProfiles(this.processedProfiles);
    load(null, this::getNegativeProfileFilter,
            addToLoad(MutablePropertySources::addFirst, true));
    addLoadedPropertySources();
}

继续跟踪initializeProfiles方法

/**
 * Initialize profile information from both the {@link Environment} active
 * profiles and any {@code spring.profiles.active{@code spring.profiles.include}
 * properties that are already set.
 */
private void initializeProfiles() {
    // The default profile for these purposes irepresented as null. We add it
    // first so that it is processed first and halowest priority.
    this.profiles.add(null);
    Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
    this.profiles.addAll(getOtherActiveProfil(activatedViaProperty));
    // Any pre-existing active profiles set viproperty sources (e.g.
    // System properties) take precedence over thosadded in config files.
    addActiveProfiles(activatedViaProperty);
    if (this.profiles.size() == 1) { // only has nulprofile
        for (String defaultProfileName this.environment.getDefaultProfiles()) {
            Profile defaultProfile = new Profi(defaultProfileName, true);
            this.profiles.add(defaultProfile);
        }
    }
}

从注释中我们可以了解到这个方法用来初始化profile。继续往下看Spring如何初始化profile。接着跟踪getProfilesActivatedViaProperty方法。

private Set<Profile> getProfilesActivatedViaProperty {
    if (!this.environment.containsProper(ACTIVE_PROFILES_PROPERTY)
            && !this.environment.containsProper(INCLUDE_PROFILES_PROPERTY)) {
        return Collections.emptySet();
    }
    Binder binder = Binder.get(this.environment);
    Set<Profile> activeProfiles = new LinkedHashSet();
    activeProfiles.addAll(getProfiles(binderINCLUDE_PROFILES_PROPERTY));
    activeProfiles.addAll(getProfiles(binderACTIVE_PROFILES_PROPERTY));
    return activeProfiles;
}

Environment目前没有读取配置文件,故这里返

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值