Java NIO2 中的 WatchService 指南

1. 概述

在本文中,我们将探讨 Java NIO.2 文件系统 API 的 WatchService 接口。这是 Java 7 中与 FileVisitor 接口一起引入的较新 IO API 中鲜为人知的功能之一。

若要在应用程序中使用 WatchService 接口,需要导入相应的类:

import java.nio.file.*;

2. 为什么使用 WatchService

了解服务功能的一个常见示例实际上是 IDE。

您可能已经注意到,IDE 总是会检测到源代码文件在自身外部发生的更改。一些 IDE 使用对话框通知您,以便您可以选择是否从文件系统重新加载文件,其他 IDE 只是在后台更新文件。

同样,较新的框架(如 Play)也会默认热重新加载应用程序代码 - 每当您从任何编辑器执行编辑时。

这些应用程序采用一种称为文件更改通知的功能,该功能在所有文件系统中都可用。

基本上,我们可以编写代码来轮询文件系统对特定文件和目录的更改。但是,此解决方案不可扩展,尤其是当文件和目录达到数百和数千个时。

在 Java 7 NIO.2 中,WatchService API 提供了一个可扩展的解决方案,用于监视目录的更改。它有一个干净的 API,并且针对性能进行了很好的优化,我们不需要实现自己的解决方案。

3. Watchservice 如何工作?

要使用 WatchService 功能,第一步是使用 java.nio.file.FileSystems 类创建 WatchService 实例:

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

接下来,我们必须创建要监视的目录的路径:

Path path = Paths.get("pathToDir");

在此步骤之后,我们必须向监视服务注册路径。在这个阶段,有两个重要的概念需要理解。StandardWatchEventKinds 类和 WatchKey 类。看看下面的注册码,只是为了了解每个跌倒的地方。我们将对此进行解释:

WatchKey watchKey = path.register(
  watchService, StandardWatchEventKinds...);

请注意这里只有两件重要的事情:首先,路径注册 API 调用将监视服务实例作为第一个参数,后跟 StandardWatchEventKinds 的变量参数。其次,注册过程的返回类型是 WatchKey 实例。

3.1. StandardWatchEventKinds

这是一个类,其实例告诉监视服务在已注册的目录上监视的事件类型。目前有四个可能的事件需要关注:

  • StandardWatchEventKinds.ENTRY_CREATE – 在监视目录中创建新条目时触发。这可能是由于创建了新文件或重命名了现有文件。
  • StandardWatchEventKinds.ENTRY_MODIFY – 在修改监视目录中的现有条目时触发。所有文件编辑都会触发此事件。在某些平台上,即使更改文件属性也会触发它。
  • StandardWatchEventKinds.ENTRY_DELETE – 在监视目录中删除、移动或重命名条目时触发。
  • StandardWatchEventKinds.OVERFLOW – 触发以指示丢失或丢弃的事件。我们不会过多关注它

3.2. WatchKey

此类表示向监视服务注册目录。当我们注册一个目录时,它的实例由监视服务返回给我们,当我们询问监视服务是否发生了我们注册的任何事件时。

监视服务不为我们提供每当事件发生时调用的回调方法。我们只能通过多种方式进行轮询以找到此信息。

我们可以使用投票 API:

WatchKey watchKey = watchService.poll();

此 API 调用将立即返回。它返回下一个排队的监视密钥,如果未发生任何事件,则返回 null。

我们还可以使用采用超时参数的重载版本:

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);

此 API 调用在返回值上与上一个 API 调用类似。但是,它会阻止超时时间单位,以提供更多可能发生事件的时间,而不是立即返回 null。

最后,我们可以使用 take API:

WatchKey watchKey = watchService.take();

最后一种方法只是阻止,直到事件发生。

我们必须在这里注意一些非常重要的事情:当 WatchKey 实例由轮询接受 API 返回时,如果不调用重置 API,它将不会捕获更多事件:

watchKey.reset();

这意味着,每次轮询操作返回监视服务实例时,都会从监视服务队列中删除该实例。重置 API 调用会将其放回队列中以等待更多事件。

观察程序服务最实际的应用需要一个循环,在这个循环中,我们不断检查监视目录中的更改并相应地进行处理。我们可以使用以下成语来实现这一点:

WatchKey key;
while ((key = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
        //process
    }
    key.reset();
}

我们创建一个监视键来存储轮询操作的返回值。while 循环将阻塞,直到条件语句返回监视键或 null。

当我们得到一个监视键时,while 循环会执行其中的代码。我们使用 WatchKey.pollEvents API 返回已发生的事件列表。然后,我们使用 a for 每个循环来逐个处理它们。

处理完所有事件后,我们必须调用重置 API 以再次将监视密钥排队。

4. 目录监视示例

由于我们已经在上一小节中介绍了 WatchService API,以及它在内部的工作原理以及如何使用它,因此我们现在可以继续查看一个完整且实用的示例。

出于可移植性的原因,我们将监视用户主目录中的活动,该目录应在所有现代操作系统上都可用。

该代码仅包含几行代码,因此我们将它保留在 main 方法中:

public class DirectoryWatcherExample {

    public static void main(String[] args) {
        WatchService watchService
          = FileSystems.getDefault().newWatchService();

        Path path = Paths.get(System.getProperty("user.home"));

        path.register(
          watchService, 
            StandardWatchEventKinds.ENTRY_CREATE, 
              StandardWatchEventKinds.ENTRY_DELETE, 
                StandardWatchEventKinds.ENTRY_MODIFY);

        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                System.out.println(
                  "Event kind:" + event.kind() 
                    + ". File affected: " + event.context() + ".");
            }
            key.reset();
        }
    }
}

这就是我们真正要做的。现在,您可以运行该类以开始监视目录。

当您导航到用户主目录并执行任何文件操作活动(如创建文件或目录、更改文件内容甚至删除文件)时,所有这些都将记录在控制台上。

例如,假设您转到用户主页,在空格中单击鼠标右键,选择“新建 - >文件”以创建一个新文件,然后将其命名为 testFile。然后添加一些内容并保存。控制台上的输出将如下所示:

Event kind:ENTRY_CREATE. File affected: New Text Document.txt.
Event kind:ENTRY_DELETE. File affected: New Text Document.txt.
Event kind:ENTRY_CREATE. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.

随意编辑路径以指向您要观看的任何目录。

5. 结论

在本文中,我们探讨了 Java 7 NIO.2 文件系统 API 中一些不太常用的功能,尤其是 WatchService 接口。

我们还设法完成了构建目录监视应用程序的步骤来演示该功能。

而且,与往常一样,本文中使用的示例的完整源代码可在 Github 项目中找到。

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值