本文源自目前公司中对一些项目的总结。
实际问题:每个工程都会包含多套配置环境,包括开发环境,测试环境,沙盒环境以及生产环境,而每一套环境都对应着不同的配置参数。
本文以一个非web工程举例,使用到了Commons Configuration + Maven管理配置,并包括一个完整的配置文件读取工具类。整理一下分享出来。
---------------------------------------
第一部分:Commons Configuration功能简记
Commons Configuration是Apache下的一个java配置管理类库项目。
支持的配置文件可以是以下多种格式:
Properties files、XML documents、Property list files(.plist)、JNDI、JDBC Datasource、System properties、Applet parameters、Servlet parameters。
基本功能简记:
一、Properties:
1、include = colors.properties //可以在properties中引用properties
2、Lists and arrays:
colors.pie = #FF0000, #00FF00, #0000FF
config.getStringArray("colors.pie") //获取数组值
3、save properties:
config.save("usergui.backup.properties"); //保存文件
4、Special Characters and Escaping:特殊字符
5、Custom properties readers and writers:自定义属性文件处理规则(key带空格情况)
二、File-based Configurations:
1、Automatic Saving: //setProperties时自动保存
config.setAutoSave(true);
2、Automatic Reloading: //重新加载文件
config.setReloadingStrategy(new FileChangedReloadingStrategy());
三、Hierarchical properties:
1、load xml file. //加载xml文件
2、Accessing structured properties: //访问某元素
tables.table(0).name
tables.table(0)[@tableType]
HierarchicalConfiguration sub = config.configurationAt("tables.table(0)");
String tableName = sub.getString("name"); // only need to provide relative path
List<Object> fieldNames = sub.getList("fields.field.name");
3、Escaping special characters: //特殊字符,如xml的key中带.
int testVal = config.getInt("test..value");
4、expression engine/XPATH
5、Validation of XML configuration files
四、Composite Configuration
Setting Up Defaults
Saving Changes
五、Configuration Events:
listeners are notified whenever the configuration's data is changed.
This provides an easy means for tracking updates on a configuration.
1、Configuration listeners
2、Error listeners
六、Utility classes:
1、Copy a configuration
2、Converting a flat configuration into a hierarchical one: //转换flag为hierarchical结构
PropertiesConfiguration flatConfig = new PropertiesConfiguration();
flatConfig.load(...);
HierarchicalConfiguration hc = ConfigurationUtils.convertToHierarchical(flatConfig);
3、Handling of runtime exceptions:
throws a runtime exception (namely a ConfigurationRuntimeException) on each received error event.
以上只是部分功能。
详情见官方参考资料:
user guide: http://commons.apache.org/configuration/userguide/user_guide.html
java api doc:http://commons.apache.org/configuration/apidocs/index.html
其它类似资料:
commons configuration包使用:
http://www.360doc.com/content/11/0406/16/2749105_107609102.shtml
第二部分:一个配置文件工具类的封装
曾经写过一篇blog,一个数据库配置文件工具类:http://shensy.iteye.com/blog/1566651
这次,完善了一下上次那个程序,写了个更通用的实现。
- public final class ConfigProject {
- public static final Logger LOGGER = LoggerFactory.getLogger(ConfigProject.class);
- public static CompositeConfiguration COMMON_CONFIG = new CompositeConfiguration();
- public static void init(String confDir)
- {
- //加载顺序是从前到后,一个属性在先加载的配置中查找,没有则在后面的配置中查找.
- COMMON_CONFIG.addConfiguration(loadPropertiesConfiguration(confDir,"settings.properties"));
- COMMON_CONFIG.addConfiguration(loadXMLConfiguration(confDir,"data.xml"));
- }
- /**
- * 获取properties配置文件属性
- */
- public static PropertiesConfiguration loadPropertiesConfiguration(String confDir,String fileName){
- PropertiesConfiguration fileConfiguration = new PropertiesConfiguration();
- try{
- fileConfiguration.setReloadingStrategy(getFileChangedReloadingStrategy());
- //加载文件前设置分隔符失效(不使用任何分隔符).
- fileConfiguration.setDelimiterParsingDisabled(true);
- //加载文件前设置分割符,默认为逗号(,)如果配置项中包含分隔符就会被分割.
- //fileConfiguration.setListDelimiter('_');
- //如果用户没有指定绝对路径:加载文件顺序为current directory,user home directory,classpath.
- fileConfiguration.load(getConfigURL(confDir,fileName));
- }catch(Exception e){
- LOGGER.error("failed to load properties config:" + fileName, e);
- }
- return fileConfiguration;
- }
- /**
- * 获取XML配置文件属性
- */
- public static XMLConfiguration loadXMLConfiguration(String confDir,String fileName){
- XMLConfiguration xmlConfiguration = new XMLConfiguration();
- try{
- xmlConfiguration.setReloadingStrategy(getFileChangedReloadingStrategy());
- xmlConfiguration.setDelimiterParsingDisabled(true);
- xmlConfiguration.setAttributeSplittingDisabled(true);
- xmlConfiguration.load(getConfigURL(confDir,fileName));
- }catch(Exception e){
- LOGGER.error("failed to load xml config:" + fileName, e);
- }
- return xmlConfiguration;
- }
- /**
- * 通过properties文件设置system属性.
- */
- public static void setSystemConfigurationByProp(String fileName) throws Exception{
- SystemConfiguration systemConfiguration = new SystemConfiguration();
- systemConfiguration.setSystemProperties(fileName);
- }
- private static URL getConfigURL(String confDir,String fileName) throws Exception{
- File file = new File(confDir + "/" + fileName);
- URL url = null;
- if (file.exists()) {
- url = file.toURI().toURL();
- } else {
- /**
- * ConfigurationUtils.locate:
- * Return the location of the specified resource by searching
- * the user home directory, the current classpath and the system classpath.
- */
- url = ConfigurationUtils.locate(confDir,fileName);
- LOGGER.debug("config file url:"+url);
- }
- return url;
- }
- private static FileChangedReloadingStrategy getFileChangedReloadingStrategy(){
- FileChangedReloadingStrategy reloadingStrategy = new FileChangedReloadingStrategy();
- reloadingStrategy.setRefreshDelay(10000);//10s
- return reloadingStrategy;
- }
- public static void main(String[] args){
- ConfigProject.init("./conf");
- }
- }
上面的工具类实现了对一个properties文件和一个xml文件的加载,并设置自动重新加载时间为10s,并设置默认分隔符失效。
具体项目中可根据需要修改init中需要加载的配置文件和confDir路径即可(本例中为./conf)。
至于confDir配置文件夹路径,起到一个覆盖配置的作用:
因为本项目部署时采用jar形式部署,使用Maven管理,配置文件放在src/main/resources下。打jar包时会将src/main/resources下的配置文件直接打到jar中。但是如果线上部署时需要修改配置文件的话,就需要重新打包,比较麻烦。而使用confDir配置文件夹路径的作用是先加载confDir路径下的配置文件,如果没有该路径或没有配置文件再加载jar包中的。这样只要在部署路径下建一个conf文件夹,将修改后的同名配置文件放进去,即可覆盖jar中的配置。
第三部分:结合Maven实现配置管理
在Maven中的pom.xml文件中配置如下(举例):
首先,添加依赖:
- <dependency>
- <groupId>commons-configuration</groupId>
- <artifactId>commons-configuration</artifactId>
- <version>1.6</version>
- </dependency>
其次:
- <profiles>
- <profile>
- <id>dev</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- <properties>
- <dburl><![CDATA[dburl_dev]]></dburl>
- <username>username1</username>
- <password>dev_password</password>
- </properties>
- </profile>
- <profile>
- <id>prod</id>
- <properties>
- <dburl><![CDATA[dburl_prod]]></dburl>
- <username>username2</username>
- <password>prod_password</password>
- </properties>
- </profile>
- </profiles>
在properties文件中或xml文件中使用${}替换实际的value:
- dburl=${dburl}
- <username>${username}</username>
- <password>${password}</password>
以上建立了2套配置,一套是dev环境的,一套是prod环境的。
在用Maven命令打包时指定环境:
mvn clean package -Pdev
mvn clean package -Pprod
mvn install(将构建包安装到local repository)
mvn install -DskipTests -Pdev
mvn install -DskipTests -Pprod
(关于maven的pom.xml配置文件和maven命令的详细介绍后续会专门写一篇blog总结整理出来。)
OK,再看看打好的包,比如test.jar中的配置文件是不是对应的dev或prod的配置了。
部署时,如果test.jar部署在~/local/Test下,那么就可以在Test下在建立一个文件夹conf,并在~/local/Test/conf下面放置与jar中同名的xml或properties配置文件,这样就可以直接覆盖掉jar中相同key的配置了,相当方便。
希望对看到的人有所帮助,也希望大家多提意见。