1. 程式人生 > >【Spring Boot】(7)、配置檔案載入位置

【Spring Boot】(7)、配置檔案載入位置

Spring Boot啟動會掃描以下位置的application.properties/yml檔案作為Spring Boot預設配置檔案:

  • 外接,在相對於應用程式執行目錄的/config子目錄裡

  • 外接,在應用程式執行的目錄裡

  • 內建,在resources/config包內

  • 內建,在classpath根目錄(resouces目錄下)

原始碼解析

ConfigFileApplicationListener.class

public class ConfigFileApplicationListener
		implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {

    //...
    
    //預設配置檔名
    private static final String DEFAULT_NAMES = "application";

    // 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/";
    
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    
    //...
}

ConfigFileApplicationListener.load()

public void load() {
        this.propertiesLoader = new PropertySourcesLoader();
        this.activatedProfiles = false;
        this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
        this.processedProfiles = new LinkedList<Profile>();

        //上節講述過,會獲取當前profiles啟用的環境,預設為default
        Set<Profile> initialActiveProfiles = initializeActiveProfiles();
        this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
        if (this.profiles.isEmpty()) {
            for (String defaultProfileName : this.environment.getDefaultProfiles()) {
                Profile defaultProfile = new Profile(defaultProfileName, true);
                if (!this.profiles.contains(defaultProfile)) {
                    this.profiles.add(defaultProfile);
                }
            }
        }

        this.profiles.add(null);

        while (!this.profiles.isEmpty()) {
            Profile profile = this.profiles.poll();
            //獲取配置檔案的查詢路徑
            for (String location : getSearchLocations()) {
                if (!location.endsWith("/")) {
                    // location is a filename already, so don't search for more
                    // filenames
                    load(location, null, profile);
                }
                else {
                    for (String name : getSearchNames()) {
                        load(location, name, profile);
                    }
                }
            }
            this.processedProfiles.add(profile);
        }

        addConfigurationProperties(this.propertiesLoader.getPropertySources());
    }

ConfigFileApplicationListener.getSearchLocations()

private Set<String> getSearchLocations() {
    Set<String> locations = new LinkedHashSet<String>();
    //使用者自定義的配置檔案,優先載入,通過spring.config.location指定
    if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
        for (String path : asResolvedSet(
            this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
            if (!path.contains("$")) {
                path = StringUtils.cleanPath(path);
                if (!ResourceUtils.isUrl(path)) {
                    path = ResourceUtils.FILE_URL_PREFIX + path;
                }
            }
            locations.add(path);
        }
    }
    
    //獲取查詢路徑
    locations.addAll(
        asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
                      DEFAULT_SEARCH_LOCATIONS));
    return locations;
}

ConfigFileApplicationListener.asResolvedSet()

private Set<String> asResolvedSet(String value, String fallback) {
    List<String> list = Arrays.asList(StringUtils.trimArrayElements(
        StringUtils.commaDelimitedListToStringArray(value != null
                                                    ? this.environment.resolvePlaceholders(value) : fallback)));
    //將元素進行反轉,所以查詢的路徑實際跟DEFAULT_SEARCH_LOCATIONS所定義的順序剛好相反。
    Collections.reverse(list);
    return new LinkedHashSet<String>(list);
}

ConfigFileApplicationListener.getSearchNames()

private Set<String> getSearchNames() {
    //如果環境中有以spring.config.name的配置,則以該值作為配置檔名
    if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
        return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
                             null);
    }
	//獲取預設的配置檔名,預設為application
    return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}

PropertySourcesLoader.getAllFileExtensions()

public Set<String> getAllFileExtensions() {
    Set<String> fileExtensions = new LinkedHashSet<String>();
    //獲取所有配置檔案的字尾名,分別是properties,xml,yml,yaml
    for (PropertySourceLoader loader : this.loaders) {
        fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
    }
    return fileExtensions;
}

所以其實Spring Boot支援四種配置檔案格式:

  • properties

  • xml

  • yml

  • yaml

    而xml用的少,yml和yaml其實是一樣的格式,所以常用的是properties和yml。

PropertySourcesLoader.doLoadIntoGroup()

private PropertySource<?> doLoadIntoGroup(String identifier, String location,
				Profile profile) throws IOException {
    Resource resource = this.resourceLoader.getResource(location);
    PropertySource<?> propertySource = null;
    StringBuilder msg = new StringBuilder();
    if (resource != null && resource.exists()) {
        String name = "applicationConfig: [" + location + "]";
        String group = "applicationConfig: [" + identifier + "]";
        propertySource = this.propertiesLoader.load(resource, group, name,
                                                    (profile == null ? null : profile.getName()));
        if (propertySource != null) {
            msg.append("Loaded ");
            handleProfileProperties(propertySource);
        }
        else {
            msg.append("Skipped (empty) ");
        }
    }
    else {
        msg.append("Skipped ");
    }
    msg.append("config file ");
    msg.append(getResourceDescription(location, resource));
    if (profile != null) {
        msg.append(" for profile ").append(profile);
    }
    if (resource == null || !resource.exists()) {
        msg.append(" resource not found");
        this.logger.trace(msg);
    }
    else {
        this.logger.debug(msg);
    }
    return propertySource;
}

總結

  • 分別從file:./config/, file:./, classpath:/config/, classpath:/ 這四個位置依次讀取字尾名為properties, xml, yml, yaml的配置檔案

  • 優先順序由高到低,對於相同的屬性配置,高優先順序的配置會覆蓋優先順序低的配置;對於其他不同的屬性配置,則會進行互補。

  • 優先順序相同的情況下,同時有application.properties和application.yml,那麼application.properties裡面的屬性就會覆蓋application.yml裡的屬性,因為properties比yml優先載入。

​在檢視原始碼的時候,在getSearchLocations()程式碼中,提示可以通過spring.config.location來改變預設的配置檔案位置:專案打包好以後,使用命令列引數的形式--spring.config.location=…來啟動專案,用來指定配置檔案的新位置,從而使指定的新配置檔案和包內的預設載入的配置檔案共同起作用行程互補配置。

1java -jar spring-boot-02-config-SNAPSHOT.jar --spring.config.location=F

====================打個廣告,歡迎關注====================