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

本文主要是介绍SpringBoot外化配置源码解析:Profile处理实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于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. 感谢大家的支持!

 

这篇关于SpringBoot外化配置源码解析:Profile处理实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610617

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依