SpringBoot 2.0 系列004 --启动实战之配置文件
配置文件
配置文件加载流程
很多文档包括官方文档说SB的默认配置文件是application开头的文件,那么是为什么呢?
由上述流程我们发现,在执行SpringApplication的run方法中的prepareEnvironment子方法时,触发ConfigFileApplicationListener类中的 load方法,完成配置文件的加载
- ConfigFileApplicationListener分析
public void load() {
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 核心初始化方法 负责初始化 profile (spring.profiles.active)
// 如果没有则使用默认的AbstractEnvironment类中的(spring.profiles.default)default(可以是application-default名称的,也可以起不加)
initializeProfiles();
// 不为空时 循环加载 初始化了 理论上不会为空
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
// 第二步 此方法加载 文件的前缀 active 以及后缀 和路径
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
// 默认加载的
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
- 接 上边标注第二步的位置
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// getSearch 如果你配置了spring.config.location 路径 则使用此处路径
// 否则是用默认的 ConfigFileApplicationListener.DEFAULT_SEARCH_LOCATIONS
// 即 private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
// getSearchNames 是你配置文件的名称 ,如果配置了spring.config.name 则使用配置的这个名字
// 否则使用默认的 private static final String DEFAULT_NAMES = "application";
// 这就是默认是application这个名字的原因
Set<String> names = (isFolder ? getSearchNames() : NO_SEARCH_NAMES);
names.forEach(
// 第三步 location是路径 name是文件名 profile则是-defalut部分或者其他-dev之类的
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
- 接上边标注第三步
private void load(String location, String name, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 这里需要注意的是propertySourceLoaders 此处是在new loader的时候初始化的,即我们流程中的addPropertySources一步
//底层使用的是META-INF/spring.factories
/** org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
*/
// 这也就是 文件后缀支持 yml,yaml,properties,xml的原因
// 没有名字的情况
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile,
filterFactory.getDocumentFilter(profile), consumer);
}
}
}
// 带后缀名的情况
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
String prefix = location + name;
fileExtension = "." + fileExtension;
// 第四步 通过后缀名方式加载
loadForFileExtension(loader, prefix, fileExtension, profile,
filterFactory, consumer);
}
}
}
- 接第四步
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
String fileExtension, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
// 前边说的 -default和-dev部分
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
// prefix 是路径名+文件名
// fileExtension是.yml这种
// profile 则是第一步initializeProfiles 是this.profiles注入的
String profileSpecificFile = prefix + "-" + profile + fileExtension;
// 各处执行装载 扫描不同路径下是否有对应配置文件
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);
String description = getDescription(location, resource);
if (profile != null) {
description = description + " for profile " + profile;
}
if (resource == null || !resource.exists()) {
this.logger.trace("Skipped missing config " + description);
return;
}
if (!StringUtils.hasText(
StringUtils.getFilenameExtension(resource.getFilename()))) {
this.logger.trace("Skipped empty config extension " + description);
return;
}
String name = "applicationConfig: [" + location + "]";
// 转换成docment对象
List