A long time ago, when microservices did not appear, configuration centers did not appear, and yaml configuration files were not popular, we were all used to writing a project like ResourceUtils or PropertiesUtil The tools, whether in static methods or jsp code, have been tried and tested. Nowadays, various parameterized configurations of Springcloud, various profiles, various configuration centers of Apollo, Nacos, and various configuration formats of Properties and Yaml are your configuration file reading tools? Next, we will explain the Mendmix project's ResourceUtils tool class to open our Mendmix代码解析系列课程 chapter.

introduce

ResourceUtils runs through each life cycle of the project Mendmix and is widely used throughout the project. Currently, it supports Properties, Yaml configuration file parsing, Springcloud-compatible profile configuration, and seamless compatibility with various configuration centers such as Apollo and Nacos. At the same time, it also provides many methods for quick configuration file reading, such as: getListValue, getMapValue, getBeanValue, etc.

## code analysis
### Loading process

Load local configuration files via static code blocks
 static {
       loadLocalConfigs();
}

The loadLocalConfigs method first tries to obtain the value of spring.profiles.active . In order to be compatible with the value specified by --spring.profiles.active=prd spring.profiles.active , Mendmix provides an application startup class base BaseApplicationStarter , in this base class, various operating parameters will be processed and set as system variables.

In order to be compatible with local operation or package operation, two methods of reading the configuration are provided--- .properties loadPropertiesFromFile and loadPropertiesFromJarFile 985e09594d9b33025db170066bdc3b01---. .yaml Find out the file (you can't stop the configuration files of the two formats from being mixed), and establish the mapping relationship of <file suffix, [file name list]> as follows:

 {
  ".properties" : [ "/.../application-local.properties", "/.../application.properties" ]
}

Next, call parseSameExtensionFiles method to parse the configuration files of each suffix in turn. This method does two things: loads all the configuration files without profiles and finds out the configuration files of the profile. In order to ensure that the configuration file of the profile can override the default configuration, the configuration file of the profile found is placed in the Properties of all the parsed configuration files. This completes the entire parsing process.

Handling ${} reference configuration

Just paste the code directly, it's a bit long, similar methods are actually provided by Spring, just considering that this is just a tool class, and there are few dependencies, so I wrote it myself.

 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);
            }
        }

Integrated Configuration Center

Considering various configuration centers, we cannot bind with specific configuration center products. So Mendmix starts with the Spring loaded life cycle. After the Environment object is loaded, all configurations are merged once, the code is as follows:

 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);
        }
    }

This class is in com.mendmix.spring.helper.EnvironmentHelper , here are two points to note:

  1. Make sure merging is done before applying bean initialization
  2. It is necessary to skip local configuration merging, otherwise there may be a situation where the remote configuration and the local configuration are overwritten.

## Usage example if there is such a configuration file

 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

Partial usage

 //查询指定前缀的配置
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");

---

About Mendmix

Mendmix is based on the Apache 2.0 open source protocol and is 100% open source. Positioning is a one-stop distributed development architecture open source solution and cloud native architecture technology base. Mendmix provides database, cache, message middleware, distributed timing tasks, security framework, gateway and rapid integration capabilities of mainstream manufacturers' cloud services. Based on Mendmix, you can quickly build a distributed architecture based on microservices with high concurrency and high availability without paying attention to technical details.

Project address: github , gitee


在路上
4 声望7 粉丝

开源拥趸,5年企业级开源技术推广,6年个人开源项目持续投入。对微服务、k8s、DDD、中台建设有一定见解。欢迎交流。