基本流程图
Loader的load加载配置文件
简单的说其实内部就是加载默认的配置文件,激活环境的文件,我们来看看细节。
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
FilteredPropertySource的apply
首先默认取加载defaultProperties
属性源,但是没有,所以就调用了传进来的lambda
表达式。
initializeProfiles
定义了一些集合,用来做记录,然后我们看初始化做了什么。
这里有个想技巧,先放进一个null
,为了后面可以优先处理这个,然后就可以去加载配置文件。后面还会去添加一个default
默认的配置,也就是说此时profiles
里有null
和default
两个配置。
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
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.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY);
Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY);
List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
this.profiles.addAll(otherActiveProfiles);
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
this.profiles.addAll(includedViaProperty);
addActiveProfiles(activatedViaProperty);
if (this.profiles.size() == 1) { // only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
遍历profiles队列
这里就可以看到,遍历的时候第一个是null
,所以先处理,不会去处理默认的default
,他的思想就是如果第一次可以获取到配置,那就用这个配置,不用去获取默认的了。
isDefaultProfile
显然null
是不满足的。
load加载文件
一个函数式编程的写法,其实就是获取所有的搜索路径getSearchLocations
,挨个遍历执行lambda里面的方法,如果是个文件夹的话,搜索里面的文件名字集合,进行加载,否则就什么都不做,因为NO_SEARCH_NAMES
是空集合
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
getSearchLocations获取搜索路径
先看有没spring.config.location
属性源,有的话从这里面的找路径,没有的话再看spring.config.additional-location
属性源,最后把classpath:/,classpath:/config/,file:./,file:./config/
这些路径也加上返回,这里就说明了我们的配置文件可以放在这里路径下,不过遍历的时候是反序的,也就是说优先级应该是file:./config/, file:./, classpath:/config/, classpath:/
,项目目录优先,有config
的优先。
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
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;
}
默认就这4
个:
getSearchNames获取搜做的名字(关键点)
这里很关键,首先是取找spring.config.name
属性源,不存在的话再去找application
属性源,所以为什么配置了bootstrap
的话会先去找,因为在配置的时候加了spring.config.name=bootstrap
属性源,加载完毕后再删除,然后就可以去加载application
属性源了,其实环境准备时间出发了两次,所以这两个属性源都会被处理,这个后面说。
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
private static final String DEFAULT_NAMES = "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);
}
后面继续说拿到了配置文件名字后怎么加载。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。