SpringBoot外化配置源码解析:Profile处理实现

1496 篇文章 10 订阅
1494 篇文章 14 订阅

基于Profile 的处理实现

在日常使用中我们可以通过配置 spring.profiles.active 指定一组不同环境的配置文件,比如
application-dev.properties、application-test.properties、application-prod.properties。那么,profile 是如何被加载使用的呢?本节带大家重点分析一下 ConfigFileApplicationListener 类中基于 profile 的文件加载处理逻辑。


ConfigFileApplicationListener 类中单独定义了一个内部类 Profile 用来存储 profile 的相关信息,该类只有两个核心字段:name 用来表示 profile 文件的名称;defaultProfile 用来表示 profile 是否为默认的。

private static class Profile {
private final String name ;
private final boolean defaultProfile;}

在 ConfigFileApplicationL istener 类的逻辑处理中(除了关于配置文件的具体加载)都离不开profile 的参与。我们先从内部私有类 Loader 的 load 方法开始,代码如下。

void load() {
/过糖符合案件的 properties
FilteredPropertySource . apply(this . environment, DEFAULT PROPERTIES, LOAD_ F
ILTERED_ PROPERTY,
(defaultProperties) -> {
//创建默认的 Profile 双队列
this.profiles = new LinkedList<>();
//创建默认的已处理 Profile 列表
this . processedProfiles = new LinkedList<>
();
//默认设置为未激活
this. activatedProfiles = false;
//创建 key 为 Profile,值为 MutablePropertySo
urces 的默计 Map,注意是有序的 Map
this. loaded = new LinkedHashMap<>();
//加载配置 profile 信息, 默认为 default
initializeProfiles();
//遍历 profiles, 并加戟解析
while (!this. profiles . isEmpty()) {
Profile profile = this . profiles. poll();
//非默认的 profile 则加入
if (isDefaultProfile(profile)) {
addProfileToEnvironment (profile . getNam
e());
//解析处理 profile
load(profile, this: :getPositiveProfileFi
lter,
addToLoaded(MutablePropertySource
s: :addLast, false));
/已处理过的放入对应的列表
this. processedProfiles . add(profile);
//再次加戴 profile 为 null 的配置,将其放置在 L
oaded 的最前面
load(null, this: : getNegativeProfileFilter,
addToLoaded(Mutable-PropertySources: :addFirst, true));
//添加加载的 PropertySource 到环境中
addL oadedPropertySources();
//过滤并添加 defaul tProperties 到 processedP
rofiles 和环境中
applyActiveProfiles (defaultProperties);
});
}

以上代码执行的操作就是处理指定的 profile 与默认的 profile 之间的优先级,以及顺序关系,而其中的 load 方法是对 profile 的加载操作。

需注意的是,在 Spring Boot 2.1.x 版本中新增了 FilteredPropertySource 用来对属性文件进行过滤。同时,在 applyActiveProfiles 方 法内也涉及 Binder 类(2.2.0 新增), 它提供了关于属性配置的对象容器功能。

load 方法中 initializeProfiles 方法之前都是私有类 L oader 成员变量的初始化操作。下面我们看一 下 initializeProfiles 方法对默认 profile 的初始化操作。

private void initializeProfiles() {
//首先添加 default profile,确保首先被执行,并且优先级最低
this . profiles. add(null);
//查找环境中 spring. profiles . active 属性配置的 Profile
Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_ PROFIL
ES_ PROPERTY);
//查找环境中 spring. profiles. include 属性配置的 Profile
Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE PROFIL
ES_ PROPERTY);
//查找环境中除以上两类之外的其他属性配置的 Profile
List<Profile> otherActiveProfiles = getotherActiveProfiles (activatedViaPr
operty, includedViaProperty);
//其他属性配置添加到 profiles 队列中
this . profiles . addAll(otherActiveProfiles);
//将 included 属 性添加到队列中
this . profiles. addAll (includedViaProperty);
//将 activatedViaProperty 添加入 profiles 队列, 并没置 activatedProfiles 为激活
状态
addActiveProfiles (activatedViaProperty);
//如果没有任何 profile 配置,也 就是默认只添加了一个 null,则执行内部逻辑
if (this. profiles.size() == 1) {// AbstractEnvironment 中有默认的 default 属性, 则将 default profile 添加到 pr
ofiles 中
for (String defaultProfileName : this . environment . getDefaultProfiles())
{
Profile defaultProfile = new Profile(defaultProfileName, true);
this . profiles . add(defaultProfile);
}
}

在这个初始化的过程中,initializeProfiles 首先会给 profiles 添加一一个优先级最低的 null值,然后判断 spring. profiles active、spring .profiles include 属性配置的 profile,如果存在配置项则激活 activatedProfiles 配置。如果不存在,则 profiles 的长度为 1,进入设置默认的profile 配置。

当 initializeProfiles 方法执行完成后,程序执行回到主代码逻辑,此时会遍历 profiles 中的值,并逐一进行 load 操作。处理完成的会单独放在 processedProfiles 中,最后再次加载profile 为 null 的配置,加载 PropertySource 到环境中。

其中遍历循环过程中调用的 load 方法代码如下。

private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer)
getSearchLocat ions(). forEach( (location) -> {
boolean isFolder = location endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_ SEARCH_ NAMES;
names . forEach(
(name) -> load(location, name, profile, filterFactory, consumer));
}

在上面的代码中,主要通过 getSearchL ocations 方法获得默认的扫描路径,如果没有特殊指 定 , 就 采 用 常 量 DEFAULT_ SEARCH_ LOCATIONS中定义的4个路 径 。 而getSearchNames 方 法获得的就是 application 这个默认的配置文件名。然后,逐一遍历加载目录路径及其指定文件名的文件。

当扫描到符合条件的文件时程序会进行相应的解析操作,比如我们将指定 active 的配置放在默认的配置文件中,那么第一轮 for 循环就会将该 参数读取出来,并添加到 profiles 中,并且把 profile 中的 default 配置项移除。

private void load(PropertySourceLoader loader, String location, Profile prafile,
DocumentFilter filter, DocumentConsumer consumer) {
try {List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
f (filter .match(document))
addActiveProfiles (document . getActiveProfiles());
addInc ludedProfiles (document . getIncludeProfiles());
loaded . add(document);
}
}catch (Exception ex) {
}
}

重点看上面代码中 for 循环的操作,如果解析配置文件中获得 profile 的配置项,会对这些配置项进行再次处理,也就是调用 addActiveProfiles 方法。addActiveProfiles 方法的代码如下。

void addActiveProfiles(Set<Profile> profiles) {
//如果未经激活则将其添加到 profiles 队列中
this . profiles . addAll(profiles);
if (this . logger. isDebugEnabled()) {
this. logger. debug("Activated activeProfiles
+ StringUtils. collectionToCommaDelimitedString(profil
es));
// profile 设置被激活
this . activatedProfiles = true;
//移除未处理的默 profile
removeUnprocessedDefaultProfiles();
}

这里会将配置文件中获得的 profile 添加到 profiles 中去,并设置 profile 为激活状态。最后,再调用
removeUnprocessedDefaultProfiles 方 法将默认值移除。很显然,既然已经获得了指定的 profile 配置,那么程序自动设置的默认值也就失效了。

最后再看一下 load 方法 中 add oadedPropertySources 方法,该方法将加载的配置文件有序地设置到环境中。而配置文件有序性也是通过 loaded 的数据结构来实现的,在初始化的时候已经看到它是一个 LinkedHashMap。

private void addL oadedPropertySources() {
MutablePropertySources destination = this . environment. getPropertySources
();
List<MutablePropertySources> loaded = new ArrayL ist<>(this. loaded. values
());//倒序,后指定的 profile 在前面
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();
}
}
}
}

一般情况 下 loaded 属性中会存储两个 MutablePropertySources, -一个为默认的,一个为通过 active 指定的,而 MutablePropertySources 中又存储着 属性配置文件的路径列表。

通过上面的双层遍历会获得默认的属性配置文件和指定的属性配置文件,同时将它们添加到环境中去。

这里我们从整体了解了 Profile 的操作流程,上一 节中已经举例讲解配置文件的解析、加载等过程,不在此赘述。

本文给大家讲解的内容是SpringBoot外化配置源码解析基于Profile 的处理实现

  1. 下篇文章给大家讲解的是SpringBoot外化配置源码解析综合实战;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值