很久很久以前当微服务还没呈现、配置核心还没呈现、yaml 配置文件也还没风行的时候,咱们都习惯在我的项目外面写一个相似 ResourceUtils
或者 PropertiesUtil
的工具,无论在静态方法还是 jsp 代码都屡试不爽。现在 Springcloud 各种参数化配置、各种 profile,Apollo、Nacos 各种配置核心以及 Properties、Yaml 各种配置格局你的配置文件读取工具还好么?接下来咱们解说 Mendmix
我的项目的 ResourceUtils
工具类开启咱们 Mendmix 代码解析系列课程
的篇章。
介绍
ResourceUtils 贯通了 Mendmix
我的项目各个生命周期,在整个我的项目中被大量应用。目前反对 Properties、Yaml 配置文件解析、兼容 Springcloud 的 profile 配置形式以及无缝兼容 Apollo、Nacos 等各种各样的配置核心。同时还提供了诸多疾速配置文件读取的办法,如:getListValue,getMapValue、getBeanValue 等。
## 代码剖析
### 加载过程
通过动态代码块加载本地配置文件
static {loadLocalConfigs();
}
loadLocalConfigs 这个办法首先尝试获取 spring.profiles.active
的值,为了兼容通过 --spring.profiles.active=prd
的形式指定 spring.profiles.active
的值,Mendmix 提供了一个利用启动类基类BaseApplicationStarter
,在这个基类外面会解决各种运行参数并把设置为零碎变量。
为了兼容本地运行或打包运行提供了 loadPropertiesFromFile
和loadPropertiesFromJarFile
两个读取配置的办法,通过这两个办法会把本地的所有 .properties
及.yaml
文件找进去(你也拦不住两种格局的配置文件混用),并建设 < 文件后缀,[文件名称列表]> 的映射关系如下:
{".properties" : [ "/.../application-local.properties", "/.../application.properties"]
}
接下来调用 parseSameExtensionFiles
办法顺次循环解析每个后缀名的配置文件,这个办法做了两件事件:加载所有不带 profile 的配置文件和找出 profile 的配置文件。为了确保 profile 的配置文件能笼罩默认配置,找出的 profile 的配置文件放在做好放入解析好的所有配置文件 Properties 外面。这样整个解析过程就实现了。
解决 ${}援用配置
间接贴代码吧,有点长, 相似办法实际上 Spring 提供的有,仅仅是思考这只是一个工具类,进来少依赖,所以就本人写了。
public static String replaceRefValue(Properties properties,String value) {if(!value.contains(PLACEHOLDER_PREFIX)){return value;}
String[] segments = value.split("\\$\\{");
String seg;
StringBuilder finalValue = new StringBuilder();
for (int i = 0; i < segments.length; i++) {seg = StringUtils.trimToNull(segments[i]);
if(StringUtils.isBlank(seg))continue;
if(seg.contains(PLACEHOLDER_SUFFIX)){String refKey = seg.substring(0, seg.indexOf(PLACEHOLDER_SUFFIX)).trim();
// 其余非 ${}的占位符如:{{host}}
String withBraceString = null;
if(seg.contains("{")){withBraceString = seg.substring(seg.indexOf(PLACEHOLDER_SUFFIX)+1);
}
// 如果蕴含默认值,如:${host:127.0.0.1}
String defaultValue = null;
int defaultValSpliterIndex = refKey.indexOf(":");
if(defaultValSpliterIndex > 0){defaultValue = refKey.substring(defaultValSpliterIndex + 1);
refKey = refKey.substring(0,defaultValSpliterIndex);
}
String refValue = System.getProperty(refKey);
if(StringUtils.isBlank(refValue))refValue = System.getenv(refKey);
if(StringUtils.isBlank(refValue))refValue = properties.getProperty(refKey);
if(StringUtils.isBlank(refValue)){refValue = defaultValue;}
if(StringUtils.isBlank(refValue)){finalValue.append(PLACEHOLDER_PREFIX + refKey + PLACEHOLDER_SUFFIX);
}else{finalValue.append(refValue);
}
if(withBraceString != null){finalValue.append(withBraceString);
}else{String[] segments2 = seg.split("\\}");
if(segments2.length == 2){finalValue.append(segments2[1]);
}
}
}else{finalValue.append(seg);
}
}
整合配置核心
思考到各种各样的配置核心,所以咱们不能与具体配置核心产品绑定。所以 Mendmix 从 Spring 加载的生命周期下手。在 Environment
对象加载实现后对所有配置进行一次合并,代码如下:
private static void mergeEnvironmentProperties(){MutablePropertySources propertySources = ((ConfigurableEnvironment)environment).getPropertySources();
int count;
for (PropertySource<?> source : propertySources) {if(source.getName().startsWith("servlet") || source.getName().startsWith("system")){continue;}
if(source.getName().contains("applicationConfig: [classpath")) {continue;}
count = 0;
if (source instanceof EnumerablePropertySource) {for (String name : ((EnumerablePropertySource<?>) source) .getPropertyNames()) {Object value = source.getProperty(name);
if(value != null){ResourceUtils.add(name, value.toString());
count++;
}
}
}
System.out.println(">>merge PropertySource:" + source.getName() + ",nums:" + count);
}
}
该类在com.mendmix.spring.helper.EnvironmentHelper
, 这里要留神两点:
- 确保在利用 bean 初始化之前实现合并
- 要跳过本地配置合并,否则可能呈现近程配置又本本地配置笼罩的状况。
## 用法举例
如果有一份这样的配置文件
whitelist.ips=10.1.1.10;10.1.1.100
#aliyun OSS
mendmix.cos.adapter.type=aliyun
mendmix.cos.adapter.accessKey=5tHzzxhTs45tbUrKgTHYxxxx
mendmix.cos.adapter.secretKey=aIDWMP2pbvFjML7tYAzfPXXXXXXX
mendmix.cos.adapter.regionName=cn-guangzhou
#feign 代理
mendmix.loadbalancer.customize.mapping[mendmix-user-svc]=http://127.0.0.1:8081
mendmix.loadbalancer.customize.mapping[mendmix-order-svc]=http://127.0.0.1:8082
局部用法
// 查问指定前缀的配置
Properties properties = ResourceUtils.getAllProperties("mendmix.cos.adapter");
// 查问指定前缀并返回对象
CosConfig cosConfig = ResourceUtils.getBean("mendmix.cos.adapter", CosConfig.class);
//KV 格局的配置
Map<String, String> mappingValues = ResourceUtils.getMappingValues("mendmix.loadbalancer.customize.mapping");
// 返回列表
List<String> whitelistIps = ResourceUtils.getList("whitelist.ips");
// 多个配置兼容
ResourceUtils.getAnyProperty("key1","key2");
—
对于 Mendmix
Mendmix 基于 Apache 2.0 开源协定,100% 开源。定位是一站式分布式开发架构开源解决方案及云原生架构技术底座。Mendmix 提供了数据库、缓存、消息中间件、分布式定时工作、平安框架、网关以及支流产商云服务疾速集成能力。基于 Mendmix 能够不必关注技术细节疾速搭建高并发高可用基于微服务的分布式架构。
我的项目地址:github,gitee