本工具使用maven为包管理器,maven依赖如下
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-configuration2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
工具源码如下:
package com.szq.test.util;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger;
public enum ConfigUtil {
I;
private Map<String, BuilderAndTrigger> btMap = new HashMap<>();
/**
* 获取配置
* @param fileName 配置文件名,配置文件放在WEB-INF\classes目录下
* @return
* @throws ConfigurationException
*/
public ImmutableConfiguration getConfiguration(final String fileName) throws ConfigurationException {
BuilderAndTrigger bt = btMap.get(fileName);
FileBasedConfiguration configuration = null;
if (bt == null) { //builder不存在,生成新builder
ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder = createConfiguration(fileName);
configuration = builder.getConfiguration(); //检测builder是否成功生成,未成功生成则这里会抛出异常
PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 5, TimeUnit.SECONDS); //配置重载触发器,5秒触发一次重载检测
trigger.start();
btMap.put(fileName, new BuilderAndTrigger(builder, trigger));
} else {
configuration = bt.getBuilder().getConfiguration();
}
return ConfigurationUtils.unmodifiableConfiguration(configuration); //返回不可编辑配置,避免多线程并发问题
}
/**
* 移除不再使用的配置
* @param fileName
*/
public void removeConfiguration(final String fileName) {
BuilderAndTrigger bt = btMap.get(fileName);
if (bt != null) { //有builder和trigger存在
bt.getTrigger().stop();
btMap.remove(fileName);
}
}
/**
* 生成新配置builder
* @param fileName
* @return
*/
private ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> createConfiguration(final String fileName) {
ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder = null;
Parameters params = new Parameters();
PropertiesBuilderParameters proParam = params.properties();
File file = new File(fileName);
proParam.setFile(file); //这里在下文有其他说明,请细看
builder = new ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class).configure(proParam);
return builder;
}
/**
* @comment 内部类,表示 builder 和其对应的 trigger
*/
private static class BuilderAndTrigger {
private ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
private PeriodicReloadingTrigger trigger;
public BuilderAndTrigger(ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder, PeriodicReloadingTrigger trigger) {
super();
this.builder = builder;
this.trigger = trigger;
}
public ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getBuilder() {
return builder;
}
public PeriodicReloadingTrigger getTrigger() {
return trigger;
}
@Override
public String toString() {
return "BuilderAndTrigger [builder=" + builder + ", trigger=" + trigger + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((builder == null) ? 0 : builder.hashCode());
result = prime * result + ((trigger == null) ? 0 : trigger.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BuilderAndTrigger other = (BuilderAndTrigger) obj;
if (builder == null) {
if (other.builder != null)
return false;
} else if (!builder.equals(other.builder))
return false;
if (trigger == null) {
if (other.trigger != null)
return false;
} else if (!trigger.equals(other.trigger))
return false;
return true;
}
}
}
使用示例如下:
package test;
import org.apache.commons.configuration2.Configuration;
import org.junit.Test;
import com.szq.test.util.ConfigUtil;
public class Tjt {
@Test
public void t1() {
try {
//这俩配置文件放在WEB-INF\classes目录下
String l1 = "l1.properties";
String l2 = "l2.properties";
for (int i = 0; i < 100; i++) {
Thread.sleep(1000L);
if (i % 7 == 0) {
ConfigUtil.I.removeConfiguration(l1); //移除不再使用的配置文件
ConfigUtil.I.removeConfiguration(l2);
} else {
Configuration configuration1 = ConfigUtil.I.getConfiguration(l1); //获取配置
Configuration configuration2 = ConfigUtil.I.getConfiguration(l2); //第二个配置
System.out.println(configuration1.getString("a"));
System.out.println(configuration2.getString("a"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
如上,把源码保存成java文件,直接如代码2一样获取配置,然后获取配置文件内容就行了。
本工具会自动更新配置,直接修改配置文件,过一段时间程序就会自动获取到更新后的值,这期间程序不需重启。
说明:
有人可能会这样写这行代码
proParam.setFile(file).setListDelimiterHandler(new DefaultListDelimiterHandler(',')); // , 作为数组类型值得分隔符
然后在程序里这样调用
String[] a = config.getStringArray("a"); //直接从properties类型配置文件读取数组或者列表之列的数据
这样使用是有问题的,日志会报异常,如下
java.beans.IntrospectionException: bad write method arg count: public final void org.apache.commons.configuration2.AbstractConfiguration.setProperty(java.lang.String,java.lang.Object)
查看原因,网址如下:
https://issues.apache.org/jira/browse/BEANUTILS-477
所以如果要从properties类型配置文件读取数组或者列表之类的数据,最好的办法如下
String a = config.getString("a");
String[] b = a.split(","); //看你配置文件用什么分隔符,这里就写什么分隔符
List<String> c = Arrays.asList(b); //或者更进一步,做成列表