版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LoveJavaYDJ/article/details/73521726
一、场景:
系统一些配置如控制开关,通过配置文件properties动态配置,当修改配置文件时,无需重启服务器。
这里,暂时不考虑诸如Disconf
、Consul
这样的分布式配置中心。
二、老方式:
自己启动一后台独立线程,定时检查配置文件的更新时间(老版本Tomcat自动部署也是这么干的)。
InputStream in = PropertyKit.class.getResourceAsStream("/sysConfig.properties");
properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
// ...
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
缺点也很明显:
- 1.繁琐,必须自己手动开启一个后台线程定时遍历一次目标节点并记录当前状态,然后和上一次遍历的状态对比。
- 2.效率低:效率都消耗在了遍历、保存状态、对比状态上!这是因为低版本Java无法很好的利用OS文件系统的功能。
三、简介java.nio.file.WatchService
3.1、WatchService优势:
1> 该类的对象就是操作系统原生的文件系统监控器!我们都知道OS自己的文件系统监控器可以监控系统上所有文件的变化,这种监控是无需遍历、无需比较的,是一种基于信号收发的监控,因此效率一定是最高的;现在Java对其进行了包装,可以直接在Java程序中使用OS的文件系统监控器了;
2> 获取当前OS平台下的文件系统监控器。 从FileSystems这个类名就可以看出这肯定是属于OS平台文件系统的,接下来可以看出这一连串方法直接可以得到一个文件监控器:
WatchService watcher = FileSystems.getDefault().newWatchService();
3> 操作系统上可以同时开启多个监控器,因此在Java程序中也不例外,上面的代码只是获得了一个监控器,你还可以用同样的代码同时获得多个监控器;
4> 监控器其实就是一个后台线程,在后台监控文件变化所发出的信号,这里通过上述代码获得的监控器还只是一个刚刚初始化的线程,连就绪状态都没有进入,只是初始化而已;
3.2 注意事项:
因为当使用poll或take时监控器线程就会被阻塞,因为在处理文件变化的操作可能需要挺长时间,为了防止在这段时间内又要处理其他类似的事件,因此需要阻塞监控器线程,即调用reset()表示重启该线程。
3.3、使用WatchService监听示例:
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.Properties;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
/**
*
* @author : Ares.yi
* @createTime : 2014-2-15 上午11:32:19
* @version : 1.0
* @description :
*
*/
public class PropertyKit {
private static WatchService watchService ;
private static Properties properties ;
static{
final String fileName = "sysConfig.properties";
final Resource resource = new ClassPathResource(fileName);;
try {
watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(resource.getFile().getParent());
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_CREATE);
properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e1) {
e1.printStackTrace();
}
/**独立线程*/
Thread watchThread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
WatchKey watchKey = watchService.take();
for( WatchEvent<?> event : watchKey.pollEvents() ){
if( fileName.equals(event.context().toString()) ){
properties = PropertiesLoaderUtils.loadProperties(resource);
break;
}
}
watchKey.reset();//完成一次监控就需要重置监控器一次
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
watchThread.setDaemon(true);
watchThread.start();
/**注册关闭钩子*/
Thread hook = new Thread(new Runnable() {
@Override
public void run() {
try {
watchService.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
Runtime.getRuntime().addShutdownHook(hook);
}
public static String getPro(String key){
return properties.getProperty(key);
}
}
- 1> 使用WatchService监听properties文件所在目录内容的变化,包括修改、删除事件。
- 2> 通过后台线程实现阻塞等待内容变化事件,一旦发现有变更,则重新装载properties文件。
- 3> 注册关闭钩子,当JVM停止时一定关闭WatchService。
3.4、参考资料:
- 1> [疯狂Java]NIO.2:WatchService、WatchKey(监控文件变化)
- 2> https://docs.oracle.com/javase/tutorial/essential/io/notification.html