源代码参见: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.location
、spring.config.additional-location
的值时,配置文件的默认扫描路径(包含顺序)如下:
- file:./config/
- file:./
- classpath:/config/
- classpath:/
2.配置文件的处理器
//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader
...
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
...
在默认情况下配置文件处理器列表(包含顺序)如下:
- org.springframework.boot.env.PropertiesPropertySourceLoader.PropertiesPropertySourceLoader
- org.springframework.boot.env.YamlPropertySourceLoader.YamlPropertySourceLoader
以上配置文件处理器 PropertiesPropertySourceLoader
和 YamlPropertySourceLoader
默认处理的文件后缀顺序如下:
PropertiesPropertySourceLoader
1.properties
2.xmlYamlPropertySourceLoader
1.yml
2.yaml
3.确定配置文件的加载顺序
spring中默认配置文件的文件名是 application
根据上面的内容,我们可以总结出配置文件加载的顺序如下:
file:./config/application.properties
file:./config/application.xml
file:./config/application.yml
file:./config/application.yamlfile:./application.properties
file:./application.xml
file:./application.yml
file:./application.yamlclasspath:/config/application.properties
classpath:/config/application.xml
classpath:/config/application.yml
classpath:/config/application.yamlclasspath:/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;
}
}
网友评论