/**
* 这里只针对spring.profiles声明,有特殊用法
* spring.profiles.active和spring.profiles.include就是将文件激活
*/
class ConfigFileApplicationListener {
// 开始查找对应的配置文件资源,进行解析
public void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) {
// 加载指定路径文件的资源
Resource resource = this.resourceLoader.getResource(location);
// 如果文件不存在,不处理
if (resource == null || !resource.exists()) {
return;
}
// 如果文件没有后缀名,不处理
if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
return;
}
// 设置解析到的配置属性的名称
String name = "applicationConfig: [" + location + "]";
// 开始解析配置文件,并转换为Document对象
List<Document> documents = this.loadDocuments(loader, name, resource);
// 如果没有加载到属性配置,不需要处理
if (CollectionUtils.isEmpty(documents)) {
return;
}
// 最终需要保存的属性配置对应的Document
List<Document> loaded = new ArrayList<>();
// 遍历解析好的属性配置,一般情况只会有一个
// 因为一个配置文件一般情况下只会有一个Document对象(PropertySource)
for (Document document : documents) {
// 将解析好的Document对象交给文档过滤器filter来进行过滤
// 如果过滤成功,符合条件
if (filter.match(document)) {
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
// 保存这个激活的配置信息到当前Loader对象中
this.addActiveProfiles(document.getActiveProfiles());
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
// 保存这个激活的配置信息到当前Loader对象中
this.addIncludedProfiles(document.getIncludeProfiles());
// 保存符合条件的配置属性文件
loaded.add(document);
}
}
// 将这个配置属性进行倒序
Collections.reverse(loaded);
// 如果加载到了符合条件的配置属性文件
if (!loaded.isEmpty()) {
/**
* 遍历所有的符合条件的属性文件Document对象
* 然后交给DocumentConsumer文档消费者进行处理
* 实际上就是将解析好的环境缓存到Loader的loaders对象中,loaders属性对象保存了所有符合条件的环境配置->属性配置的映射
* @see {@link Loader#addToLoaded}
*/
loaded.forEach((document) -> consumer.accept(profile, document));
}
}
// 调用配置属性加载器加载属性文件,并且将环境相关的配置信息与解析到的属性配置封装成Document对象
public List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource) {
// 创建缓存KEY
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
// 是否已经处理过
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
// 没有处理过该文件
if (documents == null) {
// 使用属性文件加载器加载属性文件
List<PropertySource<?>> loaded = loader.load(name, resource);
// 将属性配置封装为Document对象
documents = this.asDocuments(loaded);
// 缓存该Document
this.loadDocumentsCache.put(cacheKey, documents);
}
// 返回解析转换后的Document对象
return documents;
}
/**
* 将解析好的配置文件对应的配置属性转换为Document对象
*
* @param loaded 加载好的配置文件对应的配置属性
*/
public List<Document> asDocuments(List<PropertySource<?>> loaded) {
// 遍历当前配置文件中加载的所有属性
return loaded.stream().map((propertySource) -> {
// 将这个加载的配置属性,使用配置属性绑定器绑定到指定的对象中
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource), this.placeholdersResolver);
// 从当前加载的配置文件对应的属性对象中,将spring.profiles.active的属性绑定到对应的对象中
Set<Profile> activeProfiles = this.getProfiles(binder, ACTIVE_PROFILES_PROPERTY);
// 从当前加载的配置文件对应的属性对象中,将spring.profiles.include的属性绑定到对应的对象中
Set<Profile> includeProfiles = this.getProfiles(binder, INCLUDE_PROFILES_PROPERTY);
// 从当前加载的配置文件对应的属性对象中,将spring.profiles的属性绑定到对应的数组对象中
/**
* @see {@link Document]
*/
String[] springProfiles = binder.bind("spring.profiles", STRING_ARRAY).orElse(null);
// 将该配置文件中加载到的配置属性,激活的配置文件,包含的配置文件,和公共key(spring.profiles)的配置文件
// 注意: 当前Document仅仅表示当前解析的配置文件对应的配置信息
return new Document(propertySource, activeProfiles, includeProfiles, springProfiles);
}).collect(Collectors.toList());
}
}
// Profile配置文件解析器
class ProfilesParser {
// 解析环境配置属性值
public static Profiles parse(String... profiles) {
Profiles[] parsed = new Profiles[profiles.length];
// 一一对配置的值进行解析
for (int i = 0; i < profiles.length; i++) {
// 解析表达式
parsed[i] = parseExpression(profiles[i]) {
// 使用(),&(and),!(not),|(or)
// (dev,pro),在环境对象中,dev和pro只要有一个设置了激活,当前document配置则保留
// dev|pro,和(dev,pro)效果一样
// dev&pro,在环境对对象中,dev和pro都设置了激活的情况下,当前document配置才保留,否则忽略该配置文件属性配置
// !dev ,在环境对象中,dev没有设置激活,则保留当前document配置
StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);
}
}
// 返回解析好的环境配置信息
return new ParsedProfiles(expressions, parsed);
}
// 生成关系为or条件的Profiles
// 使用环境配置进行or运算
public static Profiles or(Profiles... profiles) {
return new Profiles() {
@Override
public boolean matches(Predicate<String> activeProfile) {
// 遍历所有的环境配置,所有的环境配置都任何一个匹配成功就算符合条件
return Arrays.stream(profiles).anyMatch(ProfilesParser.this.isMatch(activeProfile));
}
};
}
// 生成关系为and条件的Profiles
// 使用环境配置进行and运算
public static Profiles and(Profiles... profiles) {
return new Profiles() {
@Override
public boolean matches(Predicate<String> activeProfile) {
// 遍历所有的环境配置,所有的环境配置都需要匹配成功才算符合条件
return Arrays.stream(profiles).allMatch(ProfilesParser.this.isMatch(activeProfile));
}
};
}
// 生成关系为not条件的Profiles
// 使用环境配置进行非运算
public static Profiles not(Profiles profiles) {
return new Profiles() {
@Override
public boolean matches(Predicate<String> activeProfile) {
// 将给定的环境配置进行匹配,再进行取反
return !profiles.matches(activeProfile);
}
};
}
// 真实使用环境配置进行匹配
// 直接利用给定的激活环境匹配器对环境进行匹配,所有的关系最终都是走向这里
public static Profiles equals(String profile) {
return new Profiles() {
@Override
public boolean matches(Predicate<String> activeProfile) {
// 直接利用给定的激活环境匹配器对环境进行匹配,所有的关系最终都是走向这里
return activeProfile.test(profile);
}
};
}
/**
* 返回一个对指定Profiles进行匹配的匹配器
* {@link ParsedProfiles#matches(Predicate)}
* {@link ProfilesParser#and(Profiles...)}
* {@link ProfilesParser#or(Profiles...)}
* {@link ProfilesParser#not(Profiles)}
* 最终一定会落到{@link ProfilesParser#equals(String)}中
* {@link ProfilesParser#isMatch(Predicate)}
*/
public static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {
return new Predicate() {
@Override
public boolean test(Profiles profiles) {
return profiles.matches(activeProfile);
}
};
}
}
// Profile环境配置的工具类实现类
class ParsedProfiles implements Profiles {
// spring.profiles中写的表达式字符串
public final String[] expressions;
// 解析完成的表达式对象,内部包含环境配置通过表达式描述的关系
public final Profiles[] parsed;
public ParsedProfiles(String[] expressions, Profiles[] parsed) {
this.expressions = expressions;
this.parsed = parsed;
}
/**
* 使用解析好的表达式与激活配置环境的匹配器进行一一匹配
*
* @param activeProfiles 激活配置环境的匹配器
*/
public boolean matches(Predicate<String> activeProfiles) {
// 遍历解析到表达式进行逻辑匹配
for (Profiles candidate : this.parsed) {
// 使用给定的匹配器进行匹配
// 实际上,activeProfiles才是真正进行匹配的条件
// candidate是matches是根据表达式的关系进行逻辑运算,最终通过activeProfiles来处理,得到匹配结果
/**
* {@link ProfilesParser#and(Profiles...)}
* {@link ProfilesParser#or(Profiles...)}
* {@link ProfilesParser#not(Profiles)}
* 最终一定会落到{@link ProfilesParser#equals(String)}中
* {@link ProfilesParser#isMatch(Predicate)}
*/
if (candidate.matches(activeProfiles)) {
return true;
}
}
return false;
}
}
// Profile环境配置的工具类
interface Profiles {
// 解析给定的环境配置表达式
public static Profiles of(String... profiles) {
// 解析spring.profiles的值,因为该值可以是一个表达式
return ProfilesParser.parse(profiles);
}
/**
* 条件匹配
* {@link ParsedProfiles#matches(Predicate)}
* {@link ProfilesParser#and(Profiles...)}
* {@link ProfilesParser#or(Profiles...)}
* {@link ProfilesParser#not(Profiles)}
* 最终一定会落到{@link ProfilesParser#equals(String)}中
* {@link ProfilesParser#isMatch(Predicate)}
*/
boolean matches(Predicate<String> activeProfiles);
}
// 解析完的配置文件,包装为文档类型
class Document {
// 解析到配置文件的所有属性值
public final PropertySource<?> propertySource;
/**
* @see {@link ProfilesParser}
* @see {@link ParsedProfiles#matches(Predicate)}
* 从当前加载的配置文件对应的属性对象中,获取spring.profiles的属性值
* 使用(),&(and),!(not),|(or)
* (dev,pro),在环境对象中,dev和pro只要有一个设置了激活,当前document配置则保留
* dev|pro,和(dev,pro)效果一样
* dev&pro,在环境对对象中,dev和pro都设置了激活的情况下,当前document配置才保留,否则忽略该配置文件属性配置
* !dev ,在环境对象中,dev没有设置激活,则保留当前document配置
* <p>
* 总结: 当配置文件中写了spring.profiles=xxx表达式,则满足这个表达式的条件,该配置文件才会生效,否则会被排除
* 例如: application.properties中配置了spring.profiles = dev&pro,那么,只有dev和pro配置都被激活的情况下,application.properties才会被加载
*/
public String[] profiles;
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
public final Set<Profile> activeProfiles;
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
public final Set<Profile> includeProfiles;
public Document(PropertySource<?> propertySource, String[] profiles, Set<Profile> activeProfiles, Set<Profile> includeProfiles) {
this.propertySource = propertySource;
this.profiles = profiles;
this.activeProfiles = activeProfiles;
this.includeProfiles = includeProfiles;
}
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles的属性值
public String[] getProfiles() {
return profiles;
}
// 解析到配置文件的所有属性值
public PropertySource<?> getPropertySource() {
return propertySource;
}
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
public Set<Profile> getActiveProfiles() {
return activeProfiles;
}
// 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
public Set<Profile> getIncludeProfiles() {
return includeProfiles;
}
}
SpringBooot配置中spring.profiles配置剖析
于 2024-04-11 23:59:02 首次发布