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)); } }
运行单元测试类,结果如下
源码下载Github https://github.com/Mov-hh/java-demo