Properties文件是流行的应用程序配置文件。当然,Commons Configuration支持这种格式并显著增强了基础的java.util.Properties类。本文介绍PropertiesConfiguration类的特性。注意PropertiesConfiguration是实现Configuration接口非常典型的例子,本文描述许多特性(例如,list处理或插值)其它配置类也支持。这是因为Commons Configuration附带的大多数配置实现派生自实现这些特性的共同基类AbstractConfiguration。
1 使用PropertiesConfiguration
让我们开始使用以下内容作为名为usergui.properties的简单配置文件的内容:
# 定义GUI的属性
colors.background = #FFFFFF
colors.foreground = #000080window.width = 500
window.height = 300
加载改文件:
Configuration config = new PropertiesConfiguration("usergui.properties");
如果你不指定一个绝对路径,文件将自动搜索以下位置:
- 当前目录
- 用户目录
- 类路径
替代使用需要一个文件名称的构造函数,你能调用load()方法之一。有多种重载变体允许你从各种源加载配置。
配置文件加载之后,你能通过Configuration接口的方法访问它的内容:
String backColor = config.getString("colors.background");
Dimension size = new Dimension(config.getInt("window.width"), config.getInt("window.height"));
2 包含
如果一个属性名为“include”,并且属性的值是磁盘上的一个文件名,将被包括到配置中。下面是一个例子:
# usergui.properties
include = colors.properties
include = sizes.properties
# colors.properties
colors.background = #FFFFFF
3 List和数组
Commons Configuration有能力返回一个值列表。例如一个配置文件能包含逗号分隔的值的列表:
# chart colors
colors.pie = #FF0000, #00FF00, #0000FF
你不需要手动分隔值,你能直接检索一个数组或java.util.List:
String[] colors = config.getStringArray("colors.pie");
List<Object> colorList = config.getList("colors.pie");
要么,你能在你的配置文件中在多行使用相同键指定值列表:
# chart colors
colors.pie = #FF0000;
colors.pie = #00FF00;
colors.pie = #0000FF;
所有描述在AbstractConfiguration的关于list处理特性同样适用于配置文件,包括改变list分隔符或禁用list处理。
4 保存
为了保存你的配置,只需调用save()方法:
PropertiesConfiguration config = new PropertiesConfiguration("usergui.properties");
config.setProperty("colors.background", "#000000);
config.save();
你也能保存配置副本到其它文件:
PropertiesConfiguration config = new PropertiesConfiguration("usergui.properties");
config.setProperty("colors.background", "#000000);
config.save("usergui.backup.properties);// 保存到相对于usergui.properties的路径
5 特殊字符和转义
如果你需要在属性中包含特殊字符像换行符、制表符或Unicode字符,你能使用与Java字符串相同的转义字符指定它。list分隔符(默认是逗号),也能转义:
key = This \n string \t contains \, escaped \\ characters \u0020
当处理包含反斜线字符(例如,Windows系统上的文件路径)的元素列表时转义规则会变得很复杂。首先要记住的是,为了得到一个反斜杠,你必须写两个:
config.dir = C:\\Temp\\
这个问题不是Commons Configuration导致的,而是关联配置文件的标准格式。参考java.util.Properties的load()方法的Javadocs。现在如果你想要使用文件路径定义一个列表,你可能会这么写:
# 定义文件目录列表的错误的方式
config.dirs = C:\\Temp\\,D:\\data\\
正如注释所述,这将不能工作。第一个目录后面的反斜线解释为list分隔符的转义字符。因此不是带有两个元素的list,仅仅是单个属性值被定义——显然不是期望的。为了获取正确的list后面的反斜线必须被转义。通过复制它实现(是的,在配置文件中,意味着,现在我们需要4个反斜线):
# 定义目录列表的正确方式
config.dirs = C:\\Temp\\\\,D:\\data\\
因此,在属性值中4个反斜线解释为一个反斜线转移符并最终计算为单个反斜线。当一个配置文件引用网络共享名称时产生其它问题。通常这些名称以两个反斜线开始,如此明显的方式来定义这些属性如下:
# 定义网络共享的list的错误方式
config.dirs = \\\\share1,\\\\share2
不幸的是,这将不能工作,因为共享包含4个反斜线序列。因此当读取config.dirs属性的值一个list带有两个以一个反斜线开头的元素。为了解决这个问题,转义符被重复——我们现在需要8个反斜线:
# 定义网络共享list的正确方式
config.dirs = \\\\\\\\share1,\\\\\\\\share2
显而易见,转义序列会变的非常复杂并且不可读。在这种情况下,推荐使用定义list的替代方式处理:使用相同的键多次。在这种情况下,没有额外的反斜线转义(除了通常需要复制配置文件)是必须的,因为没有list分隔符被解析。
使用这种语法网络共享list看起来如下所示:
# 定义网络共享list的直接方式
config.dirs = \\\\share1
config.dirs = \\\\share2
6 布局对象
每个PropertiesConfiguration对象都关联一个布局对象,一个PropertiesConfigurationLayout类的实例。该布局对象负责保存被加载配置文件的大部分结构。这意味着像注释或空行在保存配置文件时与源文件类似(算法不是100%完美,但对于大多数用例应该足够了)。
通常,开发不需要处理这些布局对象。然而,有一些方法可能感兴趣是否增强在配置文件的输出控制是必须的。以下列表描述这些方法:
- setComment():使用该方法指定属性的注释被设置。当存储配置注释,输出属性之前,后面有一个换行符。注释可以跨域多行;在这种情况下换行字符“\n”必须作为行分隔符。
- setHeaderComment():使用setHeaderComment()配置文件的一个全局注释被设置。该注释写在文件的最开始位置,后面是一个空行。
- setBlankLinesBefore():该方法允许定义写指定属性之前的空行数。例如,它能用于分割属性文件为多个逻辑块。
- setSingleLine():如果属性有多个值,使用setSingleLine()它能指定所有这些值应该写成默认list分割的单行。它也可能写该属性的多个定义(例如,property=value1,property=value2的多行形式等)。通过PropertiesConfiguration支持,但当使用其它工具处理属性文件时可能不会工作。
- setForceSingleLine():类似于setSingleLine(),但设置全局单行标记。如果设置为true,所有属性值总是写在单行中。
- setGlobalSeparator():有可能需要定义属性分隔符。这能使用setGlobalSeparator()完成。这是一个指定能用作分隔符的任意字符串。(注意:为了产生有效的配置文件,只有字符=和:应该用作分隔符,前面和后面可以使用或不使用空格),但是方法不强制。
- setSeparator():该方法类似于setGlobalSeparator(),但允许设置特定属性的属性分隔符。
- setLineSeparator():使用该法方法能指定行分隔符。默认使用特定平台的行分隔符(例如,Unix是\n)。
PropertiesConfigurationLayout的默认设置选择保留原始配置文件的大部分布局。用上述方法可以执行特定的布局限制。
7 自定义配置读取器和输出器
有些情况下更多的控制读写属性文件的过程是必要的。例如,一个应用程序可能必须以特定格式处理一些遗留配置文件,PropertiesConfiguration不支持开箱即用,当必须不能修改。在这种情况下,可能为配置文件注入自定义读取器和输出器。
每个默认配置文件通过(定义在PropertiesConfiguration中的)嵌套类PropertiesReader和PropertiesWriter读取和写出。这些类是常规读取器和输出器类(都派生自java.io包的基类)提供一些额外的方法更便利的处理配置文件。配置文件读取器和输出器的自定义实现必须扩展这些基类。
为了安装自定义属性读取器或输出器,PropertiesConfiguration提供IOFactory接口(这也是一个嵌套类)。一个对象实现该接口存储在每个PropertiesConfiguration实例中。无论何时,配置文件的读取或写出(例如,当load()或save()方法被调用时)IOFactory对象访问创建属性读取器或写出器。
IOFactory接口相当简单;它定义方法创建一个属性读取器和属性输出器。当没有使用PropertiesConfiguration指定IOFactory时,默认实现调用DefaultIOFactory。为了更具体的讨论,提供一个注入自定义属性读取器的例子。我们加载一个配置文件的键必须包含空格。默认PropertiesConfiguration不支持。
Background Color = #800080
Foreground Color = #000080
第一步是创建一个能处理这一属性的自定义属性读取器实现。类派生自PropertiesConfiguration.PropertiesReader并覆盖parseProperty()方法:
public class WhitespacePropertiesReader extends PropertiesConfiguration.PropertiesReader
{
public WhitespacePropertiesReader(Reader in, char delimiter)
{
super(in, delimiter);
}
/**
* 解析带有空格的属性键的特殊算法。该方法每次从配置文件读取没有注释的行调用。
*/
protected void parseProperty(String line)
{
// 只在第一个“=”字符分隔行(在生产代码中这应该更健壮)
int pos = line.indexOf(‘=‘);
String key = line.substring(0, pos).trim();
String value = line.substring(pos + 1).trim();// 现在存储属性的键和值
initPropertyName(key);
initPropertyValue(value);
}
}
注意调用initPropertyName()和initPropertyValue()方法。这导致解析操作被存储。下一步是提供IOFactory接口的特定实现返回新的属性读取器类。我们只希望替换属性读取器(和使用标准输出器),可以派生DefaultIOFactory实现,因此必须只覆盖createPropertiesReader()方法。
public class WhitespaceIOFactory extends PropertiesConfiguration.DefaultIOFactory
{
/**
* 返回我们制定的属性读取器
*/
public PropertiesReader createPropertiesReader(Reader in, char delimiter)
{
return new WhitespacePropertiesReader(in, delimiter);
}
}
最后我们必须创建一个新的IOFactory实现的实例,并传入PropertiesConfiguration对象。这必须在load()方法调用之前完成。因此我们不能使用构造函数加载数据。设置配置对象的代码应该如下所示:
PropertiesConfiguration config = new PropertiesConfiguration();
config.setIOFactory(new WhitespaceIOFactory());
config.setFile(...); // 设置加载的文件
config.load();