文档章节

关于解析配置文件的一点思考

杨尚川
 杨尚川
发布于 2015/04/21 23:22
字数 1208
阅读 1507
收藏 30

假设我们有一个配置文件config.properties,取自APDPlat的主配置文件:

#主配置文件

#是否启用WEB目录文件增加和删除监控
watch.directory.enable=true

#用户密码安全策略
user.password.strategy=passwordLengthStrategy;passwordComplexityStrategy

#如果启用数据库配置,则数据库中的配置信息有最高优先级,会覆盖配置文件的配置信息
config.db.enable=true
#用配置文件中的信息强行覆盖数据库中的配置信息
config.db.override=false

#使用哪一个模块,数据库配置中会用到此变量
module.short.name=apdplat

我们怎么写程序解析呢?很多人会告诉你使用java.util.Properties:

首先,准备容器:Properties props = new Properties();

其次,准备资源:ClassPathResource cr = new ClassPathResource("/org/apdplat/config.properties");

再次,加载资源:props.load(cr.getInputStream());

最后,使用配置:String value = props.getProperty(“watch.directory.enable”);

但是这种方式真的好吗?上面的步骤中,如果配置项包含中文,我们在加载资源之前还要执行一个步骤,执行命令:

native2ascii config.properties

用命令生成的结果来替换原来的配置:

#\u4e3b\u914d\u7f6e\u6587\u4ef6

#\u662f\u5426\u542f\u7528WEB\u76ee\u5f55\u6587\u4ef6\u589e\u52a0\u548c\u5220\u9664\u76d1\u63a7
watch.directory.enable=true

#\u7528\u6237\u5bc6\u7801\u5b89\u5168\u7b56\u7565
user.password.strategy=passwordLengthStrategy;passwordComplexityStrategy

#\u5982\u679c\u542f\u7528\u6570\u636e\u5e93\u914d\u7f6e\uff0c\u5219\u6570\u636e\u5e93\u4e2d\u7684\u914d\u7f6e\u4fe1\u606f\u6709\u6700\u9ad8\u4f18\u5148\u7ea7\uff0c\u4f1a\u8986\u76d6\u914d\u7f6e\u6587\u4ef6\u7684\u914d\u7f6e\u4fe1\u606f
config.db.enable=true
#\u7528\u914d\u7f6e\u6587\u4ef6\u4e2d\u7684\u4fe1\u606f\u5f3a\u884c\u8986\u76d6\u6570\u636e\u5e93\u4e2d\u7684\u914d\u7f6e\u4fe1\u606f
config.db.override=false

#\u4f7f\u7528\u54ea\u4e00\u4e2a\u6a21\u5757\uff0c\u6570\u636e\u5e93\u914d\u7f6e\u4e2d\u4f1a\u7528\u5230\u6b64\u53d8\u91cf
module.short.name=apdplat

可以看到,我们所有的中文注释都被转码了,我们人可看不懂啥意思啊,那怎么还原呢?看如下命令:

native2ascii config.properties | native2ascii -reverse

通过这个命令的输出你就知道,可以使用native2ascii -reverse命令来还原,但是,配置文件的好处就在于方便修改配置,现在被转换成这样,是不是很糟糕呢?肯定是了,那么怎么办呢?我们可以先还原原来的文本,然后把文本文件保存为utf-8格式,接着改变加载资源的方式,指定文件编码:

props.load(new InputStreamReader(cr.getInputStream(), "utf-8"));

这样就解决了。

更进一步,如果我们不用JDK内置的Properties,要自己实现,我们该如何编写配置文件解析程序呢?

我们使用UTF-8编码的普通文本文件,不用转码,在内存中使用HashMap来保存配置信息,如下代码所示,来自APDPlatPropertyHolder类:

package org.apdplat.module.system.service;

import org.apdplat.platform.log.APDPlatLogger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apdplat.platform.log.APDPlatLoggerFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * 系统配置
 * @author 杨尚川
 */
public class PropertyHolder {
    private static final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(PropertyHolder.class);
    private static final Map<String, String> PROPERTIES = new HashMap<>();

    static {
        init();
    }

    public static Map<String, String> getProperties() {
        return PROPERTIES;
    }
    private static void load(InputStream inputStream, Map<String, String> map){
        try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"))){
            String line;
            while((line = reader.readLine()) != null){
                line = line.trim();
                if("".equals(line) || line.startsWith("#")){
                    continue;
                }
                int index = line.indexOf("=");
                if(index==-1){
                    LOG.error("错误的配置:"+line);
                    continue;
                }
                if(index>0 && line.length()>index+1) {
                    String key = line.substring(0, index).trim();
                    String value = line.substring(index + 1, line.length()).trim();
                    map.put(key, value);
                }else{
                    LOG.error("错误的配置:"+line);
                }
            }
        } catch (IOException ex) {
            LOG.error("配置文件加载失败:" + ex.getMessage());
            throw new RuntimeException(ex);
        }
    }
    /**
     * 本方法中的日志只能输出中文,因为APDPlatLoggerImpl中默认指定输出中文
     * 只有配置项加载完毕,调用了指定日志输出语言方法LOG.setLocale(getLogLanguage())
     * 之后,配置的日志输出语言才会生效
     */
    private static void init() {
            String systemConfig="/org/apdplat/config.properties";
            String localConfig="/config.local.properties";
            String dbConfig="/org/apdplat/db.properties";
            String localDBConfig="/db.local.properties";
            ClassPathResource cr = null;
            try{
                cr = new ClassPathResource(systemConfig);
                load(cr.getInputStream(), PROPERTIES);
                LOG.info("装入主配置文件:"+systemConfig);
            }catch(Exception e){
                LOG.info("装入主配置文件"+systemConfig+"失败!", e);
            }
            try{
                cr = new ClassPathResource(localConfig);
                load(cr.getInputStream(), PROPERTIES);
                LOG.info("装入自定义主配置文件:"+localConfig);
            }catch(Exception e){
                LOG.info("装入自定义主配置文件"+localConfig+"失败!", e);
            }            
            try{
                cr = new ClassPathResource(dbConfig);
                load(cr.getInputStream(), PROPERTIES);
                LOG.info("装入数据库配置文件:"+dbConfig);
                LOG.info("Database profile is loaded:"+dbConfig);
            }catch(Exception e){
                LOG.info("装入数据库配置文件"+dbConfig+"失败!", e);
            }      
            try{  
                cr = new ClassPathResource(localDBConfig);
                load(cr.getInputStream(), PROPERTIES);
                LOG.info("装入自定义数据库配置文件:"+localDBConfig);
            }catch(Exception e){
                LOG.info("装入自定义数据库配置文件"+localDBConfig+"失败!",e);
            }      
            
            String extendPropertyFiles = PROPERTIES.get("extend.property.files");
            if(extendPropertyFiles!=null && !"".equals(extendPropertyFiles.trim())){
                String[] files=extendPropertyFiles.trim().split(",");
                for(String file : files){
                    try{  
                        cr = new ClassPathResource(file);
                        load(cr.getInputStream(), PROPERTIES);
                        LOG.info("装入扩展配置文件:"+file);
                    }catch(Exception e){
                        LOG.info("装入扩展配置文件"+file+"失败!",e);
                    }      
                }
            }    
            LOG.info("系统配置属性装载完毕");
            LOG.info("******************属性列表***************************");
            PROPERTIES.keySet().forEach(propertyName -> {
                LOG.info("  " + propertyName + " = " + PROPERTIES.get(propertyName));
            });
            LOG.info("***********************************************************");
            
            //指定日志输出语言
            LOG.setLocale(getLogLanguage());
    }
    /**
     * 日志使用什么语言输出
     * @return 
     */
    public static Locale getLogLanguage(){
       String language = getProperty("log.locale.language");
       return Locale.forLanguageTag(language);
    }

    public static boolean getBooleanProperty(String name) {
        String value = PROPERTIES.get(name);

        return "true".equals(value);
    }

    public static int getIntProperty(String name) {
        String value = PROPERTIES.get(name);

        return Integer.parseInt(value);
    }

    public static String getProperty(String name) {
        String value = PROPERTIES.get(name);

        return value;
    }

    public static void setProperty(String name, String value) {
        PROPERTIES.put(name, value);
    }
}





© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
加载中

评论(9)

曾建凯
曾建凯
可能我比较老土,始终觉得最好的配置文件是xml。json用于网络通信的方式来传递配置比较理想,毕竟字节数比xml要少。但xml的完整性,是毋庸置疑的。ruby on rails带起了yaml,虽然看起来极简,但其实很多问题,不同环境、使用习惯都会导致一些奇怪的问题。
milin
milin
安装一个eclipse插件不就行了,那有那么麻烦,java解析配置文件也有现成的开源工具,为什么都要自己写呢
南湖船老大
南湖船老大

引用来自“朱宏青”的评论

所有默认配置都是javabean的形式存在 解释都写在注释里
配置文件只是去修改里面的属性而已 这样在调用的时候就不用getProp这种 而是直接操作javabean
至于配置文件以什么样的形式是可以多样化 只要提供一个解析器既可
这个不可取。非技术人员没法改配置了,还得在开发环境下编译下Java。
朱宏青
朱宏青
所有默认配置都是javabean的形式存在 解释都写在注释里
配置文件只是去修改里面的属性而已 这样在调用的时候就不用getProp这种 而是直接操作javabean
至于配置文件以什么样的形式是可以多样化 只要提供一个解析器既可
杨尚川
杨尚川 博主

引用来自“char1st”的评论

用yaml 吧。
elasticsearch用的也是yaml,对于一般的配置来说,文本文件的k=v就足够了。
char1st
char1st
用yaml 吧。
宅男小何
宅男小何

引用来自“羊半仙”的评论

json配置,so ez,之前看到Golang有个解析加载配置文件的库是可以解析加载注释的
json配置简单方便,但是里面是否不能包含注释解释吧
狗头666
狗头666
json配置,so ez,之前看到Golang有个解析加载配置文件的库是可以解析加载注释的
yanick
yanick
79
mybatis核心组件详解——MapperAnnotationBuilder

MapperAnnotationBuilder(org.apache.ibatis.builder.annotation.MapperAnnotationBuilder),mapper注解构建器。 它的职责很简单,就是解析指定的mapper接口对应的Class对象中,包含的所有...

拉风小野驴
2016/02/29
680
2
java 常见中文乱码问题解决

关于中文乱码: 一、 1).在jsp页面上输入中文,要保证中文不乱码,有三个前提: 保证contentType=“text/html;charset=UTF-8”,pageEncoding=“UTF-8” charset和pageEncoding的编码一致,...

祁猛
2016/11/07
85
0
这么火爆的引流脚本,你不知道脚本引流效果好吗?

有很多人在问脚本引流效果好吗?关于这个问题,没有一个确切的答案,对于会玩的人来说,效果好得没话说,对于不会的人来说,效果就是惨不忍睹了,为什么呢?比如哪些大咖们,用脚本引流软件引...

dsdf231
2018/04/05
0
0
关于c语言结构体成员变量访问方式的一点思考

前言 上篇博文(关于c语言结构体偏移的一点思考)对c语言中结构体偏移做了一些思考,发现博文中还有一些小的问题,没有描述的足够清楚,所以才萌生了本篇博文的想法。 为什么不直接将本篇博文作...

算法与编程之美
2013/06/27
6.1K
11
arp information Linux

蓝森林 http://www.lslnet.com 2006年7月26日 13:28 show arp看到的信息,请教一下? 关于ARP的一点东西 - 若雨 - 追求局域网内大部在10.130.4内网段,其他的192.168.0.0的没有用,10.130.5就...

MtrS
2016/06/06
14
0

没有更多内容

加载失败,请刷新页面

加载更多

一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器

一、前言 最近花了一个月时间完成了一套基于Spring Boot+Vue+Shiro前后端分离的代码生成器,目前项目代码已基本完成 止步传统CRUD,进阶代码优化: 该项目可根据数据库字段动态生成 controll...

郑清
10分钟前
0
0
javascript-十六进制随机颜色

<script> // 编写一个函数,获得一个十六进制的随机颜色的字符串(如#20CD4F) // function randomColor(){ // var r = random(0,255).toString(16); // var g = random(0,255).toString(16......

ACKo
11分钟前
1
0
springBoot +mybatis 出现sql 语句在数据库可以查询到,但是赋值到实体类上就没有的情况?

1.不要老是反复查看自己是否写错了,为啥有的能出来有的出不来? 可以查看配置文件中是否配置全: 如果在application.yml 文件中是如下配置: mybatis: mapper-locations: classpath:mapp...

kuchawyz
24分钟前
1
0
正则表达式

一、RegExp对象 进行验证和查找的API 1、创建对象: (1)用/创建(直接量):var reg=/正则/ig,表达式固定不变时使用 (2)用new创建:var reg=new RegExp(‘正则’,‘ig’),表达式需要...

wytao1995
24分钟前
1
0
实战限流(guava的RateLimiter)

关于限流 常用的限流算法有漏桶算法和令牌桶算法,guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取...

程序员欣宸
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部