很久很久以前当微服务还没呈现、配置核心还没呈现、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,在这个基类外面会解决各种运行参数并把设置为零碎变量。

为了兼容本地运行或打包运行提供了loadPropertiesFromFileloadPropertiesFromJarFile两个读取配置的办法,通过这两个办法会把本地的所有 .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,这里要留神两点:

  1. 确保在利用bean初始化之前实现合并
  2. 要跳过本地配置合并,否则可能呈现近程配置又本本地配置笼罩的状况。

## 用法举例
如果有一份这样的配置文件

whitelist.ips=10.1.1.10;10.1.1.100#aliyun OSSmendmix.cos.adapter.type=aliyunmendmix.cos.adapter.accessKey=5tHzzxhTs45tbUrKgTHYxxxxmendmix.cos.adapter.secretKey=aIDWMP2pbvFjML7tYAzfPXXXXXXXmendmix.cos.adapter.regionName=cn-guangzhou#feign代理mendmix.loadbalancer.customize.mapping[mendmix-user-svc]=http://127.0.0.1:8081mendmix.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