上一篇,我们谈到Java中的几种读取properties配置文件的方式,但是在生产环境中,最忌讳的就是重启应用了。比如某个系统的路径常量或者接口变更,需要线上及时生效,但是又不能轻易重启服务,那么如何做到平滑变更?下面就来聊一聊,如何实现配置文件的监听和重载。
实现方式
PropertiesConfiguration
PropertiesConfiguration是一个配置文件的加载工具类,封装了从配置文件里获取值并转化为基本数据类型的方,依赖commons-configuration-1.10.jar。
PropertyUtil.java:
package com.itstyle.web.util;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
/**
* 读取配置文件,并实现对配置文件的动态修改和自动保存(开关配置)
* 创建者 科帮网
* 创建时间 2017年5月12日
*
*/
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);
}
}
WatchService JDK7+
该类的对象就是操作系统原生的文件系统监控器!我们都知道OS自己的文件系统监控器可以监控系统上所有文件的变化,这种监控是无需遍历、无需比较的,是一种基于信号收发的监控,因此效率一定是最高的。现在Java对其进行了包装,可以直接在Java程序中使用OS的文件系统监控器了。
配置监听 ResourceListener.java
package com.itstyle.web.common.listener;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.acts.web.common.utils.LogUtil;
import com.acts.web.common.utils.PropertyUtil;
/**
* 监控配置文件变化
* 创建者 科帮网
* 创建时间 2017年5月12日
*
*/
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();
}
}
}
}
工具类 PropertyUtil.java
package com.itstyle.web.common.utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* properties文件获取工具类
* 创建者 科帮网
* 创建时间 2017年5月12日
*
*/
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();
}
}
总结
对比以上两种实现方式,各有优劣,大家可以根据实际情况选择合适的方式。
当然如果涉及的应用或者系统较多,就应该考虑使用配置中心进行统一配置了。