一般情况下,配置文件是系统在启动时加载的,当配置文件发生改动后需要重新启动系统。但是,在多数业务里,需要动态加载配置文件已经成为了标配,那如何实现动态加载呢?我们一一列举。
一、archaius
archaius是Netflix公司开源项目之一,基于java的配置管理类库,主要用于多配置存储的动态获取。主要功能是对apache common configuration类库的扩展。在云平台开发中可以将其用作分布式配置管理依赖构件。同时,它有如下一些特性:
- 动态获取属性
- 高效和线程安全的配置操作
- 配置改变时提供回调机制
- 可以通过jmx操作配置
- 复合配置
示例:
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.4</version>
</dependency>
public class Main {
//获取一个Long型的动态配置项,默认值是1000。
private static DynamicLongProperty timeToWait =
DynamicPropertyFactory.getInstance().getLongProperty("lock.waitTime", 1000);
public static void main(String[] args) throws Exception {
//设置回调
timeToWait.addCallback(() -> {
System.out.println("timeToWait callback, new value: " + timeToWait.get());
});
//每秒将timeToWait动态配置值打印到控制台,timeToWait.get()会动态的获取最新的配置
for (int i = 0; i < 100000; i++) {
Thread.sleep(1000);
System.out.println("timeToWait: " + timeToWait.get());
}
}
}
archaius默认读取classpath下的config.properties,所以在resources目录下增加config.properties文件,文件中增加配置:
lock.waitTime=4
修改config.properties后,默认等1分钟后会生效,可以通过jvm参数修改:
-Darchaius.fixedDelayPollingScheduler.delayMills=2000 -Darchaius.fixedDelayPollingScheduler.initialDelayMills=2000
上述是读取Long型数据,同样还有:DynamicStringProperty、DynamicDoubleProperty...
详情参考:http://techblog.ppdai.com/2018/05/08/20180508/
二、PropertiesConfiguration
PropertiesConfiguration是Apache开元的一个配置文件的加载工具类,如下:
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.6</version>
</dependency>
public class PropertyUtil {
private PropertyUtil() {
};
private static class SingletonHolder {
private static PropertiesConfiguration propertyUtil = init("config.properties");
}
public static PropertiesConfiguration getProps() {
return SingletonHolder.propertyUtil;
}
/**
* 初始化
*
* @param propertiesFile
* @see
*/
private static PropertiesConfiguration init(String propertiesFile) {
PropertiesConfiguration PropertyUtil = null;
try {
PropertyUtil = new PropertiesConfiguration(propertiesFile);
// 自动重新加载
PropertyUtil
.setReloadingStrategy(new FileChangedReloadingStrategy());
// 自动保存
PropertyUtil.setAutoSave(true);
} catch (ConfigurationException e) {
e.printStackTrace();
}
return PropertyUtil;
}
/**
* 根据Key获得对应的value
*
* @param key
* @return
* @see
*/
public static Object getProperty(String key) {
return getProps().getProperty(key);
}
/**
* 设置属性
*
* @param key
* @param value
* @see
*/
public static void setProperty(String key, String value) {
getProps().setProperty(key, value);
}
}
测试:
public static void main(String...strings) throws InterruptedException {
for (int i = 0; i < 100000; i++) {
Thread.sleep(1000);
System.out.println("timeToWait: " + PropertyUtil.getProperty("lock.waitTime"));
}
}
修改配置文件后立即生效。
参考:https://blog.52itstyle.vip/archives/885/
三、WatchService JDK7+
该类的对象就是操作系统原生的文件系统监控器!我们都知道OS自己的文件系统监控器可以监控系统上所有文件的变化,这种监控是无需遍历、无需比较的,是一种基于信号收发的监控,因此效率一定是最高的。现在Java对其进行了包装,可以直接在Java程序中使用OS的文件系统监控器了。
1、ResourceListener :
public class ResourceListener {
private static ExecutorService fixedThreadPool = Executors
.newFixedThreadPool(5);
private WatchService ws;
private String listenerPath;
private ResourceListener(String path) {
try {
ws = FileSystems.getDefault().newWatchService();
this.listenerPath = path;
start();
} catch (IOException e) {
e.printStackTrace();
}
}
private void start() {
fixedThreadPool.execute(new Listner(ws, this.listenerPath));
}
public static void addListener(String path) throws IOException {
ResourceListener resourceListener = new ResourceListener(path);
Path p = Paths.get(path);
p.register(resourceListener.ws, StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_CREATE);
}
public static void main(String[] args) throws IOException {
ResourceListener.addListener("E:/test");
}
}
class Listner implements Runnable {
private WatchService service;
private String rootPath;
public Listner(WatchService service, String rootPath) {
this.service = service;
this.rootPath = rootPath;
}
public void run() {
try {
while (true) {
WatchKey watchKey = service.take();
List<WatchEvent<?>> watchEvents = watchKey.pollEvents();
for (WatchEvent<?> event : watchEvents) {
String path = event.context().toString();
if(path.endsWith("config.properties")){
LogUtil.info("[" + rootPath + "/" + event.context()+ "]文件发生了[" + event.kind() + "]事件");
PropertyUtil.init();//配置变更初始化
}
}
watchKey.reset();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
service.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2、工具类 PropertyUtil.java
public class PropertyUtil {
private PropertyUtil(){};
private static class SingletonHolder{
private static Properties props = loadProps();
}
public static Properties getProps(){
return SingletonHolder.props;
}
private static Properties loadProps(){
Properties props = new Properties();
InputStream in = null;
try {
in = PropertyUtil.class.getClassLoader().getResourceAsStream("config.properties");
props.load(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return props;
}
public static String getProperty(String key){
return getProps().getProperty(key);
}
public static String getProperty(String key, String defaultValue) {
return getProps().getProperty(key, defaultValue);
}
public static void init(){//不要随便调用(不是私有的)
SingletonHolder.props = loadProps();
}
}
参考:https://blog.52itstyle.vip/archives/885/
四、手写PropertiesUtils:
使用Properties时,每次get数据进行文件判断。
public class PropertiesUtil {
private static Properties prop;
private static Long lastModified = 0L;
/**
* 加载文件
*/
private static void init(String configFile) {
prop = new Properties();
String filepath = new File(configFile).getPath();
try {
prop.load(PropertiesUtil.class.getClassLoader().getResourceAsStream(configFile.trim()));
//prop.load(new FileInputStream(configFile.trim()));
} catch (IOException e) {
e.printStackTrace();
} finally {
File f = new File(filepath);
lastModified = f.lastModified();
}
}
private static boolean isPropertiesModified(String configFile) {
boolean returnValue = false;
File file = new File(PropertiesUtil.class.getClassLoader().getResource(configFile).getPath());
//File file = new File(configFile);
if (file.lastModified() > lastModified) {
lastModified = file.lastModified();
returnValue = true;
}
return returnValue;
}
public static String get(String configFile,String key) {
if (prop == null || isPropertiesModified(configFile)) {
init(configFile);
}
String value = prop.get(key).toString();
return value;
}
}