场景

在很多要多次读取配置文件的场所中,想要让修改文件后,尽快的读取到新的配置。

常用的解决方式

常见的解决方式一般是使用轮训的方式。就是开启一个线程定时的去轮训配置文件的最后修改时间,如果修改时间和上次不同,就重新读取配置文件的内容。

以上的方法比较浪费资源,因为你的cpu要不停的去轮转判断,而且固定的时间间隔太长的话会造成信息修改不及时的问题。

新特性描述

在java7中加入了一个新功能,目录监控,解决了上面描述的问题。这个版本提供了一个WatchService。这个类的作用就是可以去注册文件夹和相应的事件。常见的事件就是创建,删除和改变。当发生相应事件的时候,就可以去获取到发生事件的文件或者文件夹,从而达到了及时获取到变化。这样就带来一个好处,想关注配置文件的变化的时候,就在文件所在的文件夹上注册相应的事件,一般就是注册改变事件。

编写方式

所有的文件注入都是相似的。所有这个编程就是一个规范化的东西。

首先获取到WatchService对象

WatchService watcher = FileSystems.getDefault().newWatchService();

我们监控的是文件夹,所有现在文件系统的WatchService

然后我们需要获取到想注册事件的文件夹dir,是一个Path类型,然后在service上注册事件,这里我们注册一个改变事件就行。

dir.register(watcher, ENTRY_MODIFY);

然后就是通过事件的监听来获取改变的情况,WatchService可以通过take或者poll方法来轮训,take是阻塞的,poll是非阻塞的,这个根据具体场景来选择,下面选择take,这样可以省去一部分cpu的消耗。

WatchKey key = watcher.take();

通过这个方法可以获取一个watchkey,他的pollEvents方法就会返回所有的有相应的事件。我们选择使用for循环去遍历。

for (WatchEvent> event : key.pollEvents())

也可以根据这个key来获取到监控的目录

Path dir = (Path) key.watchable();

根据event强制转化为一个Path,可以根据context方法获取一个路径。

然后根据path的方法,就可以获取到改变文件的全路径了。

Path child = dir.resolve(name);

编写方式优化

由于上面的编码其实是比较有规范的,我就把这些功能直接抽象包装一下。并且分享出来

如何还需要上面的模式,就精简到下面的几行代码

final DirWatcher dw = new DirWatcher(true);
dw.registerAllEvents(Paths.get("c:/hello")).addHandler(new WatcherResultHandler() {
public void handleResult(Path path, Kind> event) {
System.out.println(path.toString() + ": " + event.toString());
}
}).processEvents();

精简后,我们只要输入监控的目录,然后实现一个WatcherResultHandler借口,完成一个回调。

handleResult(Path path, Kind> event) 这里的第一个参数就是文件,第二个参数就是相应的事件,通过上面的操作,我们就能只关心我们的业务流程,而不用写很繁琐的操作了。