参考文档
SpringBoot配置文件的加载位置实例详解
通过源码了解springboot加载application.properties过程
从SpringBoot源码分析 配置文件的加载原理和优先级
总结
spring.config.location 加载配置文件的路径
没有配置默认值为 classpath:/,classpath:/config/,file:./,file:./config/
spring.config.name 加载配置文件的文件名
没有配置默认值为 application
spring.config.additional-location 加载配置文件的额外路径
没有配置spring.config.location 才加载额外路径
加载配置文件
location+name+[-profile]+fileExtension location为路径
一定会加载 profile 为 null的情况
获取配置文件中的属性优先级
1 配置了 spring.profiles.active=dev,test 配置的顺序倒叙优先
2 文件位置 location classpath:/,classpath:/config/,file:./,file:./config/ 倒叙优先
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)
// springboot 加载配置文件的默认位置
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
/**
* 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";
}
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
//springboot run方法
org.springframework.boot.SpringApplication#run(java.lang.String...)
// 准备 ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
//广播ApplicationEnvironmentPreparedEvent事件
listeners.environmentPrepared(environment);
//加载配置文件监听执行
ConfigFileApplicationListener
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 监听 ApplicationEnvironmentPreparedEvent
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
// 监听ApplicationPreparedEvent
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
// 加载META-INF/spring.factories下的EnvironmentPostProcessor
// springboot默认有三个实现
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
//加入ConfigFileApplicationListener 也实现了EnvironmentPostProcessor
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
// 执行 EnvironmentPostProcessor的 postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) 方法
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
META-INF/spring.factories EnvironmentPostProcessor配置
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
postProcessEnvironment
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
//默认情况下 application.getResourceLoader()为空
addPropertySources(environment, application.getResourceLoader());
}
addPropertySources
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
// 添加 systemEnvironment RandomValuePropertySource
RandomValuePropertySource.addToEnvironment(environment);
//加载配置文件
new Loader(environment, resourceLoader).load();
}
private class Loader {
private final ConfigurableEnvironment environment;
private final PropertySourcesPlaceholdersResolver placeholdersResolver;
private final ResourceLoader resourceLoader;
//
private final List<PropertySourceLoader> propertySourceLoaders;
private Deque<Profile> profiles;
private List<Profile> processedProfiles;
private boolean activatedProfiles;
//存储 MutablePropertySources
private Map<Profile, MutablePropertySources> loaded;
private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 赋值 environment
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
this.environment);
// resourceLoader 为空, new DefaultResourceLoader()
this.resourceLoader = (resourceLoader != null) ? resourceLoader
: new DefaultResourceLoader();
// 加载 META-INF/spring.factories PropertySourceLoader 两个
// org.springframework.boot.env.PropertiesPropertySourceLoader
// org.springframework.boot.env.YamlPropertySourceLoader
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
}
private static class Document {
// 读取配置文件结果 PropertySource
private final PropertySource<?> propertySource;
private String[] profiles;
private final Set<Profile> activeProfiles;
private final Set<Profile> includeProfiles;
Document(PropertySource<?> propertySource, String[] profiles,
Set<Profile> activeProfiles, Set<Profile> includeProfiles) {
this.propertySource = propertySource;
this.profiles = profiles;
this.activeProfiles = activeProfiles;
this.includeProfiles = includeProfiles;
}
}
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化Profile
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (profile != null && !profile.isDefaultProfile()) {
//将profile添加到Environment activeProfiles
addProfileToEnvironment(profile.getName());
}
// DocumentFilterFactory this::getPositiveProfileFilter 方法引用
// DocumentConsumer addToLoaded 创建对象
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
// 标记已经处理过的profile
this.processedProfiles.add(profile);
}
// 重置Environment activeProfiles
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
//load Map 到 Environment
addLoadedPropertySources();
}
默认添加 null Profile
如果环境变量没有设置spring.profiles.active 添加 DefaultProfiles default
如果设置,添加已经设置的spring.profiles.active 作为Profile
未初始化 设置 activatedProfiles=true ,以后不再添加
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
// 添加 null profile
this.profiles.add(null);
// 从 Environment 属性中获取 spring.profiles.active 作为Profile
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
// 添加 Environment 已存在的 activeProfiles
this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
// 添加profiles到 Load#profiles 并设置 activatedProfiles=true
// 当activatedProfiles为true直接返回
addActiveProfiles(activatedViaProperty);
// 只有null 环境中未设置spring.profiles.active
if (this.profiles.size() == 1) { // only has null profile
// 将environment默认spring.profiles.default添加到 Load#profiles
for (String defaultProfileName : this.environment.getDefaultProfiles())
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
filterFactory 是getPositiveProfileFilter的方法引用
consumer addToLoaded创建的对象 对象中传入 MutablePropertySources::addLast 方法引用
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// 获取加载配置文件路径 location
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
// 如果是目录 获取文件名
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach(
//根据文件路径,文件名,Profile filterFactory,consumer 加载配置
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
private DocumentFilter getPositiveProfileFilter(Profile profile) {
return (Document document) -> {
if (profile == null) {
return ObjectUtils.isEmpty(document.getProfiles());
}
return ObjectUtils.containsElement(document.getProfiles(),
profile.getName())
&& this.environment
.acceptsProfiles(Profiles.of(document.getProfiles()));
};
}
addToLoaded
传入addFirst 方法引用
addToLoaded(MutablePropertySources::addFirst, true)
private DocumentConsumer addToLoaded(
BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
boolean checkForExisting) {
return (profile, document) -> {
if (checkForExisting) {
for (MutablePropertySources merged : this.loaded.values()) {
if (merged.contains(document.getPropertySource().getName())) {
return;
}
}
}
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
(k) -> new MutablePropertySources());
addMethod.accept(merged, document.getPropertySource());
};
}
1 环境中配置了 spring.config.location
取 spring.config.location配置的值,多个以逗号隔开
2 环境中没有配置 spring.config.location
取环境中配置的 spring.config.additional-location (如果有)
取默认的配置路径 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;
}
1 环境中配置了spring.config.name
取spring.config.name 配置的值,多个以逗号隔开
2 环境中没有配置 spring.config.name
取默认的文件名 application
private Set<String> getSearchNames() {
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
return asResolvedSet(property, null);
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
private void load(String location, String name, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 文件名为空
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
// location文件 PropertySourceLoader 是否支持
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile,
filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
}
// 文件名不为空
Set<String> processed = new HashSet<>();
// org.springframework.boot.env.PropertiesPropertySourceLoader
// org.springframework.boot.env.YamlPropertySourceLoader
for (PropertySourceLoader loader : this.propertySourceLoaders) {
// 获取PropertySourceLoader支持的文件
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension,
profile, filterFactory, consumer);
}
}
}
}
prefix 文件前缀 location + name
fileExtension PropertySourceLoader 支持的拓展名
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
String fileExtension, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 空的profile
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
// profile != null 情况
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension;
// 根据DocumentFilter 过滤加载配置
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile
+ fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile,
DocumentFilter filter, DocumentConsumer consumer) {
try {
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped missing config ", location, resource, profile);
this.logger.trace(description);
}
return;
}
if (!StringUtils.hasText(
StringUtils.getFilenameExtension(resource.getFilename()))) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped empty config extension ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
String name = "applicationConfig: [" + location + "]";
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped unloaded config ", location, resource, profile);
this.logger.trace(description);
}
return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
// 分级 profile过滤
if (filter.match(document)) {
// 将document.getActiveProfiles() 添加到 this.profiles
// 只添加一次 activatedProfiles 为true不再添加
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
// 添加资源到 load中
// addToLoaded
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ",
location, resource, profile);
this.logger.debug(description);
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property "
+ "source from location '" + location + "'", ex);
}
}
private List<Document> loadDocuments(PropertySourceLoader loader, String name,
Resource resource) throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
// 已经加入缓存的不再加载
if (documents == null) {
// new OriginTrackedMapPropertySource(name, properties)
List<PropertySource<?>> loaded = loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
if (loaded == null) {
return Collections.emptyList();
}
return loaded.stream().map((propertySource) -> {
Binder binder = new Binder(
ConfigurationPropertySources.from(propertySource),
this.placeholdersResolver);
return new Document(propertySource,
binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
//PropertySource中的 spring.profiles.active作为 Document activeProfiles
getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
//PropertySource中的 spring.profiles.include 作为 Document activeProfiles
getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
}).collect(Collectors.toList());
}
private void addLoadedPropertySources() {
MutablePropertySources destination = this.environment.getPropertySources();
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
// 倒序 后加入的Profile优先加入 即设置的spring.profiles.active优先于 null
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
for (MutablePropertySources sources : loaded) {
for (PropertySource<?> source : sources) {
if (added.add(source.getName())) {
// 按照集合先后顺序添加
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}