commons configuration库允许application从不同形式的配置文件中读写配置数据。
它不仅可以从properties,xml,ini文件读取配置,支持变量插值,
还可以从系统配置,Servlet参数中读取配置。
更可以通过JDBC或者JNDI数据源读写数据库中的配置。
也可以组合多种配置文件同时使用。
同时支持文件中的变量插值,自动类型转换,还可支持jexl表达式语言等,同时你可以自由定制各种行为以满足您的需求。
非常强大,使用也非常简单。
注意本文基于的是Apache Commons Configuration组件1.10版本而言的。
Configuration 2.0版本还在开发中,没有正式发布。
Configuration 2.0 SVN Checkout地址:http://svn.apache.org/viewvc/commons/proper/configuration/trunk/
但是目前为止已经发布了2.0-SANPSHOT版本,可惜不对外公布。我们可以通过以下连接获取:
2.0-SNAPSHOT:https://repository.apache.org/content/repositories/snapshots/commons-configuration/commons-configuration/2.0-SNAPSHOT/
由于2.0-SNAPSHOT接口很不完整,所以千万别看完此文后去尝试2.0版本。
以下均为基于1.X版本而言。2.0版本中的在此处介绍的有些特性还没有完全实现。
注意:任何针对1.X版本中的写操作都是非线程安全的。
例如:
Double double = config.getDouble("number");
Integer integer = config.getInteger("number");
当然你也可以通过DefaultConfigurationBuilder(新版API不再推荐使用ConfigurationFactory)和CompositeCOnfiguration组合使用多种形式的配置。(如,system配置和xml文件配置一起使用)
当然如果你有特殊需要可以继承AbstractConfiguration or AbstractFileConfiguration来读取你自己格式的配置文件,
以上所有的配置文件我们都可以一致地通过Configuration接口来进行操作。
第一种方式:
需要的类:CompositeConfiguration,
CompositeConfiguration config = new CompositeConfiguration(); config.addConfiguration(new SystemConfiguration()); config.addConfiguration(new PropertiesConfiguration("application.properties"))
第二种方式:使用DefaultConfigurationBuilder(ConfigurationFactory现在已不推荐)
DefaultConfiguration主要用于复杂项目中多个配置文件的读取。注意:DefaultConfiguration只能使用多配置文件的配置文件来初始化。
所以在使用时一定要提供一个配置文件的配置文件。对,你没看错。
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder("config.xml"); Configuration config = builder.getConfiguration(); 也可以通过如下形式:
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); builder.setFile(new File("config.xml")); Configuration config = builder.getConfiguration(true);其中config.xml是我们的配置文件的配置文件的路径,这个文件指定了需要混合的文件。内容举例:
xml version="1.0" encoding="ISO-8859-1" ?> <configuration> <system/> //指定混合系统配置属性。 <properties fileName="application.properties"/> //指定需要混合的配置文件为apllication.properties
<xml fileName="gui.xml"/> //该配置文件可用的子元素有 //xml,properties,jndi,plist,system,configuration,ini,env,configuration >
那这些属性是如何寻找的呢,即寻找这些属性的顺序是什么?要是要冲突的属性名,又是如何处理的呢?
处理方式是这样的:从最先声明的属性配置开始查找,如果找到某个属性的值就返回,否则就去下一个配置中寻找。,这样我们就知道如果存在冲突,也不会将后面的配置覆盖前面的。
configuration接口允许我们通过属性名查找,也可以在查找时选择性的提供默认值defaultValue,同时Configuration会在后台帮助我们自动完成类型转换。
configuration支持的自动类型转换:
同时我们可以对配置文件中的属性进行添加,读,写操作等
通过addProperty()添加属性,如果存在相同的属性名,则给这个属性添加一个值,而非覆盖。
clearProperty则是删除一个属性
clear()删除所有属性。
setProperty设置属性。覆盖属性值。
containsKey(String key),检查属性是否存在。
Configuration的实现并没有提供线程安全机制。所以只能确保在多线程环境下的只读安全性,
如果需要修改属性,则需要进行同步。
如果所访问的属性并不存在,组件可能会有以下两种处理方式:
对于返回值是Object类型的,返回null,如返回值为String类型
对于返回值时基本类型的,抛出NoSunchElementException异常。如返回值为long类型
对于拥有多个值的属性通过getList()或getArray获取时,它只是返回一个empty的list。
所以我们最好最获取属性时提供一个默认值。
HiberarchicalConfiguration代为层次配置的抽象
XMLConfiguration作为其实现。
#808080
#000000
#008000
${colors.header}
15
OK,Cancel,Help
try
{
XMLConfiguration config = new XMLConfiguration("tables.xml");
// do something with config
}
catch(ConfigurationException cex)
{
}
String backColor = config.getString("colors.background");
String textColor = config.getString("colors.text");
String linkNormal = config.getString("colors.link[@normal]");
String defColor = config.getString("colors.default");
int rowsPerPage = config.getInt("rowsPerPage");
List
1.root元素:在寻找属性key时是直接被忽略的,如
2.关于属性键key:我可以通过colors.background形式层次式以点分割寻找属性。
3.可以通过字符分割形式默认为comma,即逗号的形式给一个xml元素配置多个值,如
所以name属性的获取需要通过getList()来进行。
如果值确实有包含comma的必要,那就使用\转义.如果你讨厌默认的comma来分割值,你可以通过setDefaultDelimiter()(1.10API中没有这一项)来设置默认的分割符
4.由于dot点在key寻找的过程中扮演特殊地位,所以建议不要在元素名称中使用点如
- 路径为url形式,则通过URL来加载load;
- 否则路径为绝对路径形式,通过绝对路径加载;
- 否则路径为设置了base path之后的相对路径形式,则加载它;
- 否则文件为普通文件名,如果配置文件在用户的home目录,加载它;
- 否则配置文件名文普通文件名形式,在classpath中,加载它。
- 否则抛出COnfigurationException异常。
6..configuration组件运行配置文件中使用变量插值varibal interpolator。如以上的${colors.home}:
${color.home}这没有前缀,代表在本配置文件内也就是这个xml文件内寻找
第二种形式为有前缀${prefix:var}
组件内置了三个前缀:sys,const,env:
sys-用于访问系统的相关变量.
env-用于访问系统特定的环境变量
const-用于访问classpath路径下的类中的static final field.
举例:
${sys:user.home}/settings.xml
${const:java.awt.event.KeyEvent.VK_CANCEL}
${env:JAVA_HOME}
a.引入apache commons lang组件并.继承org.apache.commons.lang3.text.StrLookup
这个lookup方法将变量名作为参数传入,并返回变量值。
import org.apache.commons.lang.text.StrLookup;
public class EchoLookup extends StrLookup
{
public String lookup(String varName)
{
return "Value of variable " + varName;
}
}
b.完成这个类之后,需要将其注册到:org.apache.commons.configuration.interpol.ConfigurationInterpolator.
这样我们才能使用该类来充当变量插值前缀,才能在应用加载配置文件时正确解析。
// Place this code somewhere in an initialization section of your application
ConfigurationInterpolator.registerGlobalLookup("echo", new EchoLookup());
这段注册代码必须要在应用初始化时,或者调用创建Configuration对象之前完成,否则将不会起作用。
这样我们就可以通过以下形式使用改前缀${echo:xby}
手动保存:
PropertiesConfiguration config = new PropertiesConfiguration("usergui.properties");
config.setProperty("colors.background", "#000000");
config.save();
自动保存:
PropertiesConfiguration config = new PropertiesConfiguration("usergui.properties");
config.setAutoSave(true);
config.setProperty("colors.background", "#000000"); // the configuration is saved after this call
PropertiesConfiguration config = new PropertiesConfiguration("usergui.properties");
config.setReloadingStrategy(new FileChangedReloadingStrategy());
3.路径分割操作:
<dir > C:\\Temp\\
Configuration defaults = new PropertiesConfiguration(fileToDefaults);
Configuration otherProperties = new PropertiesConfiguration(fileToOtherProperties);
CompositeConfiguration cc = new CompositeConfiguration();
cc.addConfiguration(otherProperties);
cc.addConfiguration(fileToDefaults);
PropertiesConfiguration saveConfiguration = new PropertiesConfiguration(fileToSaveChangesIn);
Configuration cc = new CompositeConfiguration(saveConfiguration);
cc.setProperty("newProperty","new value");
saveConfiguration.save();
Configuration changes = myCompositeConfiguration.getInMemoryConfiguration();
DatabaseConfiguration config = new DatabaseConfiguration(datasource, "tablename", "key", "value");
for (Iterator i = changes.getKeys(); i.hasNext()){
String key = i.next();
Object value = changes.get(key);
config.setProperty(key,value);
}
主要类DatabaseConfiguration.
DatabaseConfiguration(DataSource datasource, String table, String keyColumn, String valueColumn)
注意:任何针对1.X版本中的写操作都是线程不安全的。
设置Synchronizer实现,
config.setSynchronizer(new ReadWriteSynchronizer());之后我们便可以使用简单的同步了,这对大部分情况下时没有问题的。但是,
为什么说是简单的同步呢?因为这是会对单个的读写操作进行同步,如果需要复杂得原子操作,则这是不能满足需求的。原因可以从源码中看出。
以下去自项目源码:
public final void addProperty(String key, Object value)
{
beginWrite(false);
try
{
fireEvent(EVENT_ADD_PROPERTY, key, value, true);
addPropertyInternal(key, value);
fireEvent(EVENT_ADD_PROPERTY, key, value, false);
}
finally
{
endWrite();
}
}
public final Object getProperty(String key)
{
beginRead(false);
try
{
return getPropertyInternal(key);
}
finally
{
endRead();
}
}
protected void beginWrite(boolean optimize)
{
getSynchronizer().beginWrite();
}
以上代码分析取自该项目源代码。
我们可以看到,对于我们的读写操作包括对List的获取及读取等操作均可以进行可重入读写锁的同步,从而达到线程安全
但是如果我们需要一次性写入或添加多个属性呢?
这是我们需要进行下一步操作的情形,使用lock().unlock()对范围进行加锁、
config.lock(LockMode.WRITE); try { config.addProperty("prop1", "value1"); ... config.addProperty("prop_n", "value_n"); } finally { config.unlock(LockMode.WRITE); }
config.lock(LockMode.READ); try { // read access to syncSupport } finally { syncSupport.unlock(LockMode.READ); }3.记住,除非你确实不需要对配置文件进行更改,你或许只是对配置文件进行读取,否则你必须要掌握这个同步操作的用法,已达到线程安全性。
复制使用copy();
properties转xml:
// Create a flat configuration PropertiesConfiguration flatConfig = new PropertiesConfiguration(); flatConfig.load(...); HierarchicalConfiguration hc = ConfigurationUtils.convertToHierarchical(flatConfig);properties文件转Properties对象
Properties processConfiguration(Properties props) { // Create a configuration for the properties for easy access Configuration config = ConfigurationConverter.getConfiguration(props); // Now use the Configuration API for manipulating the configuration data ... // Return a Properties object with the results return ConfigurationConverter.getProperties(config); }
1.表达式语言支持,jexl
2.自定义类型转换。
3.事件与监听机制
4.bean配置。