美文网首页
SpringBoot加载配置文件的优先级顺序及原理

SpringBoot加载配置文件的优先级顺序及原理

作者: userheng | 来源:发表于2020-04-24 13:54 被阅读0次

源代码参见:org.springframework.boot.context.config.ConfigFileApplicationListener

1.配置文件的扫描路径

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
  private Set<String> getSearchLocations() {
            if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
                return getSearchLocations(CONFIG_LOCATION_PROPERTY);
            }
            Set<String> locations = getSearchLocations(
                    CONFIG_ADDITIONAL_LOCATION_PROPERTY);
            locations.addAll(
                    asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
                            DEFAULT_SEARCH_LOCATIONS));
            return locations;
        }

在没有额外配置 spring.config.locationspring.config.additional-location 的值时,配置文件的默认扫描路径(包含顺序)如下:

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

2.配置文件的处理器

//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader
...
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
                    PropertySourceLoader.class, getClass().getClassLoader());
...

在默认情况下配置文件处理器列表(包含顺序)如下:

  1. org.springframework.boot.env.PropertiesPropertySourceLoader.PropertiesPropertySourceLoader
  2. org.springframework.boot.env.YamlPropertySourceLoader.YamlPropertySourceLoader

以上配置文件处理器 PropertiesPropertySourceLoaderYamlPropertySourceLoader 默认处理的文件后缀顺序如下:

PropertiesPropertySourceLoader
1.properties
2.xml

YamlPropertySourceLoader
1.yml
2.yaml

3.确定配置文件的加载顺序

spring中默认配置文件的文件名是 application

根据上面的内容,我们可以总结出配置文件加载的顺序如下:

file:./config/application.properties
file:./config/application.xml
file:./config/application.yml
file:./config/application.yaml

file:./application.properties
file:./application.xml
file:./application.yml
file:./application.yaml

classpath:/config/application.properties
classpath:/config/application.xml
classpath:/config/application.yml
classpath:/config/application.yaml

classpath:/application.properties
classpath:/application.xml
classpath:/application.yml
classpath:/application.yaml

ConfigFileApplicationListener加载环境功能的同等实现


import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 获取环境
 * <p>
 * 同SpringBoot {@link ConfigFileApplicationListener}的执行效果
 * @author hength20953
 *
 */
@Slf4j
public class ConfigurableEnvironmentFactory {

    /*
     * 默认的配置文件路径
     */
    private final static String[] configFilesSearchLoaction = new String[] { "file:./config/", "file:./",
            "classpath:/config/", "classpath:/" };

    /*
     * 默认的资源解析器
     */
    private final static List<PropertySourceLoader> propertySourceLoaders = Arrays
            .asList(new PropertiesPropertySourceLoader(), new YamlPropertySourceLoader());

    /*
     * 资源加载器
     */
    private final static ResourceLoader resourceLoader = new DefaultResourceLoader(
            ConfigurableEnvironmentFactory.class.getClassLoader());

    /*
     * 配置文件名
     */
    private final static String CONFIG_FILE_PRIFIX = "application.";

    /*
     * spring.profiles.active
     */
    private final static String profileActive = StandardEnvironment.ACTIVE_PROFILES_PROPERTY_NAME;

    private final static String profileKey = "spring.profiles";

    private final static String defaultProfile = "NULL(default)";
    
    /**
     * 获取当前项目的环境
     * @return
     */
    public static ConfigurableEnvironment getEnviroment() {

        ConfigurableEnvironment env = new StandardEnvironment();

        String[] configFileSearchLoactionInPriorityOrder = getConfigFileSearchLoactionInPriorityOrder();

        List<PropertySourceLoader> configFileSourceLoaderInPriorityOrder = getConfigFileSourceLoaderInPriorityOrder();

    
        List<PropertySource<?>> propertySources = new ArrayList<>();
        for (String configFilePath : configFileSearchLoactionInPriorityOrder) {

            load(configFilePath, configFileSourceLoaderInPriorityOrder, propertySources);
        }

        List<String[]> activeProfiles = new ArrayList<>();
        //这里的activeProfile可能来之于命令行参数指定,"-Dspring.profiles.active"
        activeProfiles.add(env.getActiveProfiles());
        
        Map<String, List<PropertySource<?>>> groupedByProfile =  propertySourcesGropByProfile(propertySources, activeProfiles);
        
        /*
         * set activeProfiles
         */
        for(int i=0,j=activeProfiles.size();i<j;i++) {
            String[] activeProfileArr = activeProfiles.get(i);
            if(activeProfileArr != null && activeProfileArr.length > 0) {
                env.setActiveProfiles(activeProfileArr);
                break;
            }
        }
        
        /*
         * set propertySources
         */
        String[] activeProfile = env.getActiveProfiles();
        if(activeProfile != null && activeProfile.length > 0) {
            //按profile定义的先后顺序
            for (String profile : activeProfile) {
                List<PropertySource<?>> p = groupedByProfile.get(profile);
                if(p != null) {
                    p.stream().forEach((propertySource) -> env.getPropertySources().addLast(propertySource));
                }
            }
        }
        
        //默认配置总在最后加入
        List<PropertySource<?>> defaultPropertySources = groupedByProfile.get(defaultProfile);
        if(defaultPropertySources != null && !defaultPropertySources.isEmpty()) {
            defaultPropertySources.stream().forEach((propertySource) -> env.getPropertySources().addLast(propertySource));
        }
        
        return env;
    }

    private static Map<String, List<PropertySource<?>>> propertySourcesGropByProfile(
            List<PropertySource<?>> propertySources, List<String[]> activeProfiles) {
        
        Map<String, List<PropertySource<?>>> propertySourcesGropByProfile = null;
        if (!propertySources.isEmpty()) {
            
            propertySourcesGropByProfile = new HashMap<>();
            
            for (PropertySource<?> e : propertySources) {
                String activeProfile = (String) e.getProperty(profileActive);
                if (StringUtils.hasText(activeProfile)) {
                    //默认以","分割
                    activeProfiles.add(activeProfile.split(","));
                }

                String profile = (String) e.getProperty(profileKey);
                profile = profile == null ? defaultProfile : profile;

                propertySourcesGropByProfile.computeIfAbsent(profile, (key) -> new ArrayList<PropertySource<?>>())
                        .add(e);
            }
        }
        
        return propertySourcesGropByProfile;
    }

    private static void load(String configFilePath, List<PropertySourceLoader> configFileSourceLoaderInPriorityOrder,
            List<PropertySource<?>> propertySources) {
        for (PropertySourceLoader propertySourceLoader : configFileSourceLoaderInPriorityOrder) {
            String[] fileExtensions = propertySourceLoader.getFileExtensions();
            load(configFilePath, fileExtensions, propertySourceLoader, propertySources);
        }
    }

    /*
     * 加载配置文件,加载顺序同加载器支持的文件名后缀的顺序 如:{"properties", "xml", "yml", "yaml"}
     */
    private static void load(String configFilePath, String[] fileExtensions, PropertySourceLoader propertySourceLoader,
            List<PropertySource<?>> propertySources) {

        for (String suffix : fileExtensions) {
            String configFileLocation = configFilePath + CONFIG_FILE_PRIFIX + suffix;

            load(configFileLocation, propertySourceLoader, propertySources);
        }
    }

    private static void load(String configFileLocation, PropertySourceLoader psl,
            List<PropertySource<?>> propertySources) {

        // 获取Resource
        Resource resource = resourceLoader.getResource(configFileLocation);
        if (resource != null && resource.exists()) {

            String propertySourceName = "applicationConfig: [" + configFileLocation + "]";

            try {

                List<PropertySource<?>> loaded = psl.load(propertySourceName, resource);

                // 若单个文件中包含多个子配置文件,优先级先倒序排列
                if (loaded.size() > 0) {
                    Collections.reverse(loaded);
                }

                propertySources.addAll(loaded);

            } catch (IOException e) {
                throw new IllegalStateException("加载配置文件失败," + "配置文件路径 '" + configFileLocation + "'", e);
            }
        } else {
            log.debug("未找到配置文件:{}", configFileLocation);
        }
    }

    /*
     * 配置文件加载路径,加载优先级同路径的顺序
     */
    private static String[] getConfigFileSearchLoactionInPriorityOrder() {
        return configFilesSearchLoaction;
    }

    /*
     * 配置文件加载器,加载优先级同加载器的顺序
     */
    private static List<PropertySourceLoader> getConfigFileSourceLoaderInPriorityOrder() {
        return propertySourceLoaders;
    }

}

相关文章

网友评论

      本文标题:SpringBoot加载配置文件的优先级顺序及原理

      本文链接:https://www.haomeiwen.com/subject/fxnxwhtx.html