1. 占位符基本使用方式
在 Apache Commons Configuration2 中,占位符(Variable Interpolation)是一种强大的功能,允许在配置值中引用其他配置属性或外部值。占位符使用 ${前缀:键名} 的格式,其中:
- 前缀:用于标识使用哪个 Lookup 实现来解析值
- 键名:传递给 Lookup 实现的查找键
1.1 基本语法
占位符的基本格式为:
${前缀:键名}
例如,在配置文件中:
properties
test.url=${classpath:cell-test.key}
test.runtime=${java:runtime}
1.2 工作原理
当配置系统遇到占位符时,会根据前缀查找对应的 Lookup 实现,然后调用其 lookup(String key) 方法获取实际值。这个过程在 ConfigurationReader 类中可以看到:
java
public class ConfigurationReader {
public ConfigurationReader() {
// 创建CombinedConfiguration
this.combinedConfig = new CombinedConfiguration();
// 设置表达式引擎为默认表达式引擎
this.combinedConfig.setExpressionEngine(DefaultExpressionEngine.instance());
// 获取StringLookup对象用于变量插值
InterpolatorStringLookup interpolator = StringLookupFactory.INSTANCE.interpolatorStringLookup();
// 注册自定义的classpath前缀Lookup
interpolator.registerLookup("classpath", ClasspathLookup.INSTANCE);
// 为CombinedConfiguration设置变量插值器
this.combinedConfig.setInterpolator(interpolator);
// ...
}
}
2. 默认提供的占位符
Apache Commons Configuration2 通过 DefaultLookups 和 DefaultStringLookup 提供了多种默认的占位符实现。
2.1 DefaultLookups 提供的占位符
DefaultLookups 是 Apache Commons Configuration2 中定义的枚举类,提供了各种内置的 Lookup 实现:
| 前缀 | 描述 | 使用示例 |
|---|---|---|
| base64Decoder | Base64 解码 | ${base64Decoder:SGVsbG8gV29ybGQ=} |
| base64Encoder | Base64 编码 | ${base64Encoder:Hello World} |
| const | 常量引用 | ${const:java.io.File.separator} |
| date | 日期格式化 | ${date:yyyy-MM-dd} |
| env | 环境变量 | ${env:PATH} |
| file | 文件内容 | ${file:/path/to/file.txt} |
| java | Java 系统属性 | ${java:runtime} |
| localhost | 本地主机信息 | ${localhost:name} |
| properties | 属性文件 | ${properties:config.properties:key} |
| resourceBundle | 资源绑定 | ${resourceBundle:messages:greeting} |
| script | 脚本执行 | ${script:javascript:1+1} |
| sys | 系统属性(同java) | ${sys:user.home} |
| url | URL内容 | ${url:http://example.com} |
| urlDecoder | URL解码 | ${urlDecoder:https%3A%2F%2Fexample.com} |
| urlEncoder | URL编码 | ${urlEncoder:https://example.com} |
| xml | XML内容 | ${xml:/path/to/file.xml:/root/child/text()} |
2.2 DefaultStringLookup 提供的占位符
DefaultStringLookup 是 Apache Commons Text 库中的枚举类,为字符串查找提供了各种实现:
| 键名 | 对应的查找器 | 描述 |
|---|---|---|
| base64Decoder | Base64DecoderStringLookup | Base64 解码 |
| base64Encoder | Base64EncoderStringLookup | Base64 编码 |
| const | ConstantStringLookup | Java 常量引用 |
| date | DateStringLookup | 日期格式化 |
| env | EnvironmentVariableStringLookup | 环境变量 |
| file | FileStringLookup | 文件内容读取 |
| java | JavaPlatformStringLookup | Java 平台信息 |
| localhost | LocalHostStringLookup | 本地主机信息 |
| properties | PropertiesStringLookup | 属性文件读取 |
| resourceBundle | ResourceBundleStringLookup | 资源绑定 |
| script | ScriptStringLookup | 脚本执行 |
| sys | SystemPropertyStringLookup | 系统属性 |
| url | UrlStringLookup | URL内容读取 |
| urlDecoder | UrlDecoderStringLookup | URL解码 |
| urlEncoder | UrlEncoderStringLookup | URL编码 |
| xml | XmlStringLookup | XML内容读取 |
3. 自定义占位符实现
3.1 实现 Lookup 接口
要创建自定义占位符,需要实现 Apache Commons Configuration2 的 Lookup 接口。以下是 ClasspathLookup 的实现示例:
java
public class ClasspathLookup implements Lookup {
public static final ClasspathLookup INSTANCE = new ClasspathLookup();
@Override
public Object lookup(String variable) {
if(Strings.isNullOrEmpty(variable)) {
return null;
}
URL resourceUrl = getClass().getClassLoader().getResource(variable);
return (resourceUrl != null) ? resourceUrl : null;
}
}
3.2 注册自定义 Lookup
实现 Lookup 接口后,需要将其注册到配置系统中。在 ConfigurationReader 类中,可以看到如何注册自定义 Lookup:
java
// 获取StringLookup对象用于变量插值
InterpolatorStringLookup interpolator = StringLookupFactory.INSTANCE.interpolatorStringLookup();
// 注册自定义的classpath前缀Lookup
interpolator.registerLookup("classpath", ClasspathLookup.INSTANCE);
// 为CombinedConfiguration设置变量插值器
this.combinedConfig.setInterpolator(interpolator);
3.3 使用自定义占位符
注册后,可以在配置文件中使用自定义占位符。根据 ClasspathLookupTest 的示例:
properties
# 在cell-config.properties中定义
test.url=${classpath:cell-test.key}
test.url2=${classpath:nofound.key}
test.runtime=${java:runtime}
3.4 测试示例
以下是测试自定义占位符的示例代码:
java
@Test
public void test1ClasspathLookup() {
// 读取通过classpath前缀定义的资源路径(URL)
String value = config.getString("test.url");
log("value:{}", value);
assertNotNull(value);
assertNotEquals("${classpath:cell-test.key}", value); // 验证占位符已被解析
// 读取为URL类型
URL urlValue = config.get(URL.class, "test.url");
log("value:{}", urlValue);
assertNotNull(urlValue);
// 测试不存在的资源(应保留原始占位符)
String notFound = config.getString("test.url2");
log("value:{}", notFound);
assertEquals("${classpath:nofound.key}", notFound); // 资源不存在时保持原样
}
4. 实现细节与注意事项
4.1 占位符解析行为
- 当 Lookup 返回
null时,原始占位符字符串会被保留 - 占位符可以嵌套使用
- 默认情况下,占位符使用
${}格式,可以自定义表达式引擎更改格式
4.2 ClasspathLookup 实现特点
- 使用单例模式(INSTANCE 常量)
- 对空输入进行处理,返回 null
- 当资源未找到时返回 null,而不是抛出异常
- 返回 URL 对象,而不是路径字符串
4.3 性能考虑
- 频繁使用的 Lookup 实现应考虑使用缓存
- 对于资源密集型操作,应确保 Lookup 实现高效
5. 总结
Apache Commons Configuration2 的占位符机制提供了灵活强大的配置管理能力:
- 通过
${前缀:键名}格式在配置中引用外部值 - 内置多种 Lookup 实现满足常见需求
- 可以通过实现 Lookup 接口轻松扩展自定义占位符
ClasspathLookup展示了如何创建从类路径加载资源的自定义占位符
通过合理使用占位符,可以构建更加灵活、可维护的配置系统,特别是在需要引用外部资源或环境变量的场景中。
6. 参考资料
-
Apache Commons Configuration2 官方文档
-
Apache Commons Text 官方文档