properties属性文件设计&实践(1) - 多个属性文件

在项目中,一般会在类路径下存在这样的一个属性文件,如:config.properties systemconfig.properties等,通过属性文件可以实现以下目的
1. 统一维护公用的配置性属性
2. 不修改class/jar,改变类的行为

一般还会针对此属性文件提供一个类来读取其属性

 

本文介绍一种在设计公用代码时的属性文件通用设计思路,即多个属性文件
1. 默认属性文件
    在公用代码工程维护,发布时直接打包到jar中,其中的属性会被2、3覆盖
2. 各工程自定义属性文件
    在公用代码工程不维护,使用者提供,其中属性会被3覆盖
3. 不可覆盖的属性文件
    公用代码工程维护,发布时直接打包到jar中

具体实现

1.Configuration 属性读取类

2.config.default.properties 默认属性文件

3.config.properties 各工程自定义属性文件

4.config.nooverride.properties 不可覆盖的属性文件

 

1.Configuration 属性读取类

 

package mov.demo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 属性读取类
 * @author mov
 * @since 0.0.1
 */
public class Configuration {
    private static Logger log = LoggerFactory.getLogger(Configuration.class);
    
    private static Properties props = new Properties();;
    
    /** 默认属性文件 */
    private static final String CONFIG_FILE_DEFAULT = "config.default.properties";
    
    /** 各工程自定义配置 */
    private static final String CONFIG_FILE_USER_DEFINED = "config.properties";
    
    /** 不可覆盖的属性文件 */
    private static final String CONFIG_FILE_NO_OVERRIDE = "config.nooverride.properties";

    static {
        int successLoadedCount = 0;
        /* 先加载默认配置 */
        if (loadConfig(CONFIG_FILE_DEFAULT)) {
            ++successLoadedCount;
        }
        
        /* 再加载用户自定义配置,以覆盖默认 */
        if (loadConfig(CONFIG_FILE_USER_DEFINED)) {
            ++successLoadedCount;
        }

        /* 最后加载不可覆盖的配置,以覆盖所有 */
        if (loadConfig(CONFIG_FILE_NO_OVERRIDE)) {
            ++successLoadedCount;
        }
        
        if (successLoadedCount == 0) {
            log.error("all config file load error");
        }
    }

    /**
     * 获取属性值
     * @param key
     * @return 属性存在时返回对应的值,否则返回""
     */ 
    public static String getCfgValue(String key) {
        return getCfgValue(key, "");
    }
    
    /**
     * 获取属性值
     * @param key
     * @param defaultValue
     * @return 属性存在时返回对应的值,否则返回defaultValue
     */ 
    public static String getCfgValue(String key, String defaultValue) {
        return props.getProperty(key, defaultValue);
    }
    
    /**
     * 获取属性值
     * @param key
     * @return 属性存在且可以转为int时返回对应的值,否则返回0
     */
    public static int getIntCfgValue(String key) {
        return getIntCfgValue(key, 0);
    }

    /**
     * 获取属性值
     * @param key
     * @param defaultValue
     * @return 属性存在且可以转为int时时返回对应的值,否则返回defaultValue
     */
    public static int getIntCfgValue(String key, int defaultValue) {
        String val = getCfgValue(key);
        if (!isEmpty(val)) {
            try {
                return Integer.parseInt(val);
            } catch (NumberFormatException e) {
                log.warn("error get config. value '{}' is not a valid int for key '{}'"
                        , val, key);
            }
        }
        
        return defaultValue;
    }
    
    /**
     * 获取属性值
     * @param key
     * @return 属性存在时返回对应的值,否则返回false
     */
    public static boolean getBooleanCfgValue(String key) {
        return getBooleanCfgValue(key, false);
    }
    
    /**
     * 获取属性值
     * @param key
     * @param defaultValue
     * @return 属性存在时返回对应的值,否则返回defaultValue
     */
    public static boolean getBooleanCfgValue(String key, boolean defaultValue) {
        String val = getCfgValue(key);
        if (!isEmpty(val)) {
            return Boolean.parseBoolean(val);
        } else {
            return defaultValue;
        }
    }

    private static boolean loadConfig(String configFile) {
        boolean success = false;
        InputStream inputStream = null;
        try {
            inputStream = Configuration.class.getClassLoader().getResourceAsStream(configFile);
            
            if (inputStream != null) {
                props.load(inputStream);
                success = true;
            } else {
                log.warn("project config file 'classpath:{}' not found", configFile);
            }
        } catch (Throwable e) {
            log.error("error load config file 'classpath:{}'", configFile, e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // do nothing
                }
            }
        }
        
        return success;
    }
    
    private static boolean isEmpty(String val) {
        return val == null || val.length() == 0;
    }
}

 

2.config.default.properties 默认属性文件

string.in.default=default
string.in.defaultAndUserdefine=default
string.in.all=default

 

3.config.properties 各工程自定义属性文件

string.in.defaultAndUserdefine=userdefine
string.in.all=userdefine

int.positive=1000
int.negative=-1000
int.overflow=2147483648
int.underflow=-2147483649

boolean.true=TruE
boolean.false=whatever

 

4.config.nooverride.properties 不可覆盖的属性文件

string.in.all=nooverride

 

单元测试

package mov.demo;

import static org.junit.Assert.*;

import org.junit.Test;

public class ConfigurationTest {

    @Test
    public void testGetCfgValueString() {
        assertEquals("", Configuration.getCfgValue("not.exists"));
        assertEquals("default", Configuration.getCfgValue("string.in.default"));
        assertEquals("userdefine", Configuration.getCfgValue("string.in.defaultAndUserdefine"));
        assertEquals("nooverride", Configuration.getCfgValue("string.in.all"));
    }

    @Test
    public void testGetCfgValueStringString() {
        assertEquals(null, Configuration.getCfgValue("not.exists", null));
        assertEquals("default", Configuration.getCfgValue("string.in.default", null));
        assertEquals("userdefine", Configuration.getCfgValue("string.in.defaultAndUserdefine", null));
        assertEquals("nooverride", Configuration.getCfgValue("string.in.all", null));
    }

    @Test
    public void testGetIntCfgValueString() {
        assertEquals(0, Configuration.getIntCfgValue("not.exists"));
        assertEquals(1000, Configuration.getIntCfgValue("int.positive"));
        assertEquals(-1000, Configuration.getIntCfgValue("int.negative"));
        assertEquals(0, Configuration.getIntCfgValue("int.overflow"));
        assertEquals(0, Configuration.getIntCfgValue("int.underflow"));
    }

    @Test
    public void testGetIntCfgValueStringInt() {
        assertEquals(-1, Configuration.getIntCfgValue("not.exists", -1));
        assertEquals(1000, Configuration.getIntCfgValue("int.positive", -1));
        assertEquals(-1000, Configuration.getIntCfgValue("int.negative", -1));
        assertEquals(-1, Configuration.getIntCfgValue("int.overflow", -1));
        assertEquals(-1, Configuration.getIntCfgValue("int.underflow", -1));
    }

    @Test
    public void testGetBooleanCfgValueString() {
        assertFalse(Configuration.getBooleanCfgValue("not.exists"));
        assertTrue(Configuration.getBooleanCfgValue("boolean.true"));
        assertFalse(Configuration.getBooleanCfgValue("boolean.false"));
    }

    @Test
    public void testGetBooleanCfgValueStringBoolean() {
        assertTrue(Configuration.getBooleanCfgValue("not.exists", true));
        assertTrue(Configuration.getBooleanCfgValue("boolean.true", false));
        assertFalse(Configuration.getBooleanCfgValue("boolean.false", true));
    }
}

 

运行单元测试类,结果如下

image

 

源码下载Github https://github.com/Mov-hh/java-demo

posted on 2015-01-23 09:12  xxt-mov  阅读(1136)  评论(0编辑  收藏  举报

导航