1. 概述
在应用部署时,我们通常需要从多种外部源提供配置信息。Apache Commons Configuration 提供了统一管理不同来源配置的解决方案。
本文将探索如何利用 Apache Commons Configuration 高效配置应用,涵盖从基础使用到高级特性的完整实践。
2. Apache Commons Configuration 简介
该库为 Java 应用提供了访问多样化配置数据的统一接口。通过配置构建器,它支持对单值和多值特性的类型化访问。
它能一致处理来自文件、数据库和 XML 等分层文档的配置属性。
2.1. Maven 依赖
首先在 pom.xml 中添加最新版本的依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
2.2. 环境准备
创建两种常见配置文件示例:
平铺格式(*.properties*):
db.host=baeldung.com
db.port=9999
db.user=admin
db.password=bXlTZWNyZXRTdHJpbmc=
db.url=${db.host}:${db.port}
db.username=${sys:user.name}
db.external-service=${const:com.baeldung.commons.configuration.ExternalServices.BAELDUNG_WEBSITE}
分层 XML 格式:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration SYSTEM "validation-sample.dtd">
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>Pattern1</pattern>
<pattern>Pattern2</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="STDOUT" />
</root>
</configuration>
3. Configurations 工具类
Configurations 工具类提供线程安全的快速启动方案,支持从不同源读取配置。可通过 Parameters 实例自定义参数。
3.1. 读取 Properties 文件
通过 Configurations 读取并访问配置:
Configurations configs = new Configurations();
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
String dbHost = config.getString("db.host");
int dbPort = config.getInt("db.port");
String dbUser = config.getString("db.user");
String dbPassword = config.getString("undefinedKey", "defaultValue");
assertEquals("baeldung.com", dbHost);
assertEquals(9999, dbPort);
assertEquals("admin", dbUser);
assertEquals("defaultValue", dbPassword);
✅ 支持类型转换(数字、List等)
✅ 可指定默认值避免异常
3.2. 读取 XML 文件
使用 XMLConfiguration 访问分层结构:
Configurations configs = new Configurations();
XMLConfiguration config = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
String appender = config.getString("appender[@name]");
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
String pattern1 = config.getString("appender.encoder.pattern(0)");
通过点号(.
)表示法遍历分层元素。
4. 基于 Properties 的配置
除工具类外,还支持通过 FileBasedConfigurationBuilder 创建配置对象:
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties()
.setFileName("src/test/resources/configuration/file1.properties"));
⚠️ 高级特性:
- 通过
include
和includeOptional
关联其他配置文件db.host=baeldung.com include=file.properties includeOptional=file2.properties // 文件不存在时不会报错
- 可自定义 IO 操作(继承 PropertiesReader/Writer)
5. 基于 XML 的配置
XMLConfiguration 支持 XML 配置解析,提供两种验证机制:
validating
:启用基础解析验证schemaValidation
:启用 Schema 验证
示例 Schema:
<!ELEMENT configuration (appender+, root)>
<!ELEMENT appender (encoder?)>
<!ATTLIST appender
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ELEMENT encoder (pattern+)>
<!ELEMENT pattern (#PCDATA)>
<!ELEMENT root (appender-ref+)>
<!ELEMENT appender-ref EMPTY>
<!ATTLIST appender-ref
ref CDATA #REQUIRED
>
验证代码:
Parameters params = new Parameters();
FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class)
.configure(params.xml()
.setFileName("src/test/resources/configuration/hierarchical.xml")
.setValidating(true));
XMLConfiguration config = builder.getConfiguration();
String appender = config.getString("appender[@name]");
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
assertEquals("STDOUT", appender);
assertEquals(2, encoderPatterns.size());
6. 多租户配置
通过 MultiFileConfigurationBuilder 支持多租户场景:
System.setProperty("tenant", "A");
String filePattern = "src/test/resources/configuration/tenant-${sys:tenant}.properties";
MultiFileConfigurationBuilder<PropertiesConfiguration> builder = new MultiFileConfigurationBuilder<>(
PropertiesConfiguration.class)
.configure(new Parameters()
.multiFile()
.setFilePattern(filePattern)
.setPrefixLookups(ConfigurationInterpolator.getDefaultPrefixLookups()));
Configuration config = builder.getConfiguration();
String tenantAName = config.getString("name");
assertEquals("Tenant A", tenantAName);
关键点:
- 文件路径包含动态参数(如
${sys:tenant}
) - 通过 DefaultPrefixLookups 解析变量
7. 数据类型处理
7.1. 缺失属性处理
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
String objectProperty = propertiesConfig.getString("anyProperty"); // 返回 null
int primitiveProperty = propertiesConfig.getInt("anyProperty", 1); // 返回默认值
assertNull(objectProperty);
assertEquals(1, primitiveProperty);
❌ 注意:
- 对象类型返回
null
- 基本类型抛出
NoSuchElementException
(除非提供默认值)
7.2. 列表与数组处理
通过分隔符处理多值属性:
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
propertiesConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
propertiesConfig.addProperty("delimitedProperty", "admin;read-only;read-write");
propertiesConfig.addProperty("arrayProperty", "value1;value2");
List<Object> delimitedProperties = propertiesConfig.getList("delimitedProperty");
String[] arrayProperties = propertiesConfig.getStringArray("arrayProperty");
⚠️ 直接获取字符串时返回第一个值:
assertEquals("value1", propertiesConfig.getString("arrayProperty"));
8. 插值与表达式
8.1. 变量插值
支持在配置中使用占位符:
System.setProperty("user.name", "Baeldung");
String dbUrl = config.getString("db.url"); // 解析 ${db.host}:${db.port}
String userName = config.getString("db.username"); // 解析 ${sys:user.name}
String externalService = config.getString("db.external-service"); // 解析常量
支持的变量类型:
- 系统属性:
${sys:...}
- 环境变量:
${env:...}
- 类常量:
${const:...}
8.2. 表达式求值
通过 JEXL 库支持表达式计算:
添加依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.1.1</version>
</dependency>
配置示例:
db.data-dump-location=${expr:System.getProperty("user.home")}/dump.dat
解析代码:
System.setProperty("user.home", "/usr/lib");
Map<String, Lookup> lookups = new HashMap<>(ConfigurationInterpolator.getDefaultPrefixLookups());
ExprLookup.Variables variables = new ExprLookup.Variables();
variables.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
ExprLookup exprLookup = new ExprLookup(variables);
exprLookup.setInterpolator(new ConfigurationInterpolator());
lookups.put("expr", exprLookup);
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(
PropertiesConfiguration.class).configure(params.properties()
.setFileName("src/test/resources/configuration/file1.properties")
.setPrefixLookups(lookups));
9. 类型转换与解码
9.1. 自动类型转换
config.addProperty("stringProperty", "This is a string");
config.addProperty("numericProperty", "9999");
config.addProperty("booleanProperty", "true");
assertEquals("This is a string", config.getString("stringProperty"));
assertEquals(9999, config.getInt("numericProperty"));
assertTrue(config.getBoolean("booleanProperty"));
❌ 转换失败抛出 ConversionException
:
config.addProperty("numericProperty", "9999a");
assertThrows(ConversionException.class,()->config.getInt("numericProperty"));
9.2. 属性解码
实现 ConfigurationDecoder 接口处理加密属性:
public class CustomDecoder implements ConfigurationDecoder {
@Override
public String decode(String encodedValue) {
return new String(Base64.decodeBase64(encodedValue));
}
}
使用解码器:
((AbstractConfiguration) config).setConfigurationDecoder(new CustomDecoder());
assertEquals("mySecretString", config.getEncodedString("db.password"));
10. 配置复制
10.1. copy() 方法
覆盖式复制:
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
Configuration subConfig = new PropertiesConfiguration();
subConfig.addProperty("db.host","baeldung");
((AbstractConfiguration) subConfig).copy(baseConfig);
String dbHost = subConfig.getString("db.host");
assertEquals("baeldung.com", dbHost); // 被覆盖
10.2. append() 方法
追加式复制:
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
Configuration subConfig = new PropertiesConfiguration();
subConfig.addProperty("db.host","baeldung");
((AbstractConfiguration) subConfig).append(baseConfig);
String dbHost = subConfig.getString("db.host");
assertEquals("baeldung", dbHost); // 保留原值
10.3. 分层配置复制
推荐方式(保持分层结构):
XMLConfiguration baseConfig = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
XMLConfiguration subConfig = new XMLConfiguration();
subConfig = (XMLConfiguration) baseConfig.clone(); // 或 new XMLConfiguration(baseConfig)
⚠️ 普通的 copy()/append()
会破坏分层结构
11. 总结
本文系统介绍了 Apache Commons Configuration 的核心能力:
✅ 基础特性:
- 统一配置访问接口
- 支持 Properties/XML 等多种格式
- 类型安全的数据访问
✅ 高级特性:
- 多租户配置管理
- 变量插值与表达式求值
- 自动类型转换与属性解码
- 配置合并与复制
适用场景:
- 需要统一管理多源配置的企业应用
- 多租户 SaaS 系统
- 复杂配置解析需求
完整代码示例见 GitHub 仓库。