本文目录导读:

我将为您提供一个完整的Java WatchService案例,包括文件监控的基本用法和一些实用场景。
基础WatchService案例
import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 文件监控器 - 基础案例
*/
public class FileWatcher {
private final WatchService watchService;
private final Map<WatchKey, Path> keyPathMap = new HashMap<>();
private volatile boolean running = true;
public FileWatcher() throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
}
/**
* 注册监控目录
*/
public void registerDirectory(Path dir) throws IOException {
// 注册监控事件:创建、修改、删除
WatchKey key = dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
keyPathMap.put(key, dir);
System.out.println("监控目录:" + dir);
}
/**
* 开始监控
*/
public void start() {
System.out.println("文件监控已启动...");
while (running) {
WatchKey key;
try {
// 阻塞等待事件
key = watchService.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("监控被中断");
return;
}
Path dir = keyPathMap.get(key);
// 处理事件
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// 检查是否溢出
if (kind == StandardWatchEventKinds.OVERFLOW) {
System.out.println("[警告] 事件溢出,部分事件可能丢失");
continue;
}
// 获取文件名
Path filename = (Path) event.context();
Path fullPath = dir.resolve(filename);
// 处理不同事件类型
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
handleCreate(fullPath);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
handleModify(fullPath);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
handleDelete(fullPath);
}
}
// 重置key以继续接收事件
boolean valid = key.reset();
if (!valid) {
// 目录已被删除
keyPathMap.remove(key);
System.out.println("目录已被删除,停止监控:" + dir);
if (keyPathMap.isEmpty()) {
break;
}
}
}
close();
}
private void handleCreate(Path path) {
System.out.println("[创建] " + path);
if (Files.isDirectory(path)) {
// 递归监控子目录(可选)
// 需要重新注册
}
}
private void handleModify(Path path) {
System.out.println("[修改] " + path);
}
private void handleDelete(Path path) {
System.out.println("[删除] " + path);
}
/**
* 停止监控
*/
public void stop() {
this.running = false;
}
/**
* 关闭资源
*/
public void close() {
try {
watchService.close();
System.out.println("文件监控已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用示例
public static void main(String[] args) throws IOException {
// 创建监控器
FileWatcher watcher = new FileWatcher();
// 注册要监控的目录
Path monitorDir = Paths.get("/tmp/monitor");
if (!Files.exists(monitorDir)) {
Files.createDirectories(monitorDir);
}
watcher.registerDirectory(monitorDir);
// 可注册多个目录
// watcher.registerDirectory(Paths.get("/home/username/Documents"));
// watcher.registerDirectory(Paths.get("/var/log"));
// 启动监控(在单独线程中运行)
Thread watcherThread = new Thread(watcher::start, "file-watcher-thread");
watcherThread.start();
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
watcher.stop();
try {
watcherThread.join(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
System.out.println("按 Ctrl+C 退出程序");
}
}
增强版:递归目录监控
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.TimeUnit;
/**
* 递归文件监控器
*/
public class RecursiveWatcher {
private final WatchService watchService;
public RecursiveWatcher() throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
}
/**
* 递归注册目录及其所有子目录
*/
public void registerAll(final Path start) throws IOException {
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
System.out.println("注册目录: " + dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* 处理事件(支持动态注册新目录)
*/
public void processEvents() throws IOException {
while (true) {
WatchKey key;
try {
key = watchService.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
Path dir = null;
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
// 获取完整的文件路径
if (dir == null) {
// 需要知道是哪个目录触发的
// 这里简化处理
}
Path child = (dir != null) ? dir.resolve(filename) : filename;
System.out.printf("%s: %s\n", kind.name(), child);
// 如果创建了新目录,自动注册
if (kind == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(child)) {
try {
registerAll(child);
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (!key.reset()) {
// 目录不可用
System.out.println("目录无法访问,移除监控");
}
}
}
}
实用案例:配置文件热更新监听器
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 配置文件热更新监听器
*/
public class ConfigWatcher {
public interface ConfigChangeListener {
void onConfigChanged(String configPath, Map<String, String> newConfig);
}
private final WatchService watchService;
private final Map<String, Map<String, String>> configCache = new ConcurrentHashMap<>();
private final Map<String, ConfigChangeListener> listeners = new ConcurrentHashMap<>();
private final ExecutorService executor = Executors.newFixedThreadPool(2);
public ConfigWatcher() throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
}
/**
* 监控配置文件
*/
public void watchConfig(String configPath) throws IOException {
Path path = Paths.get(configPath);
Path parent = path.getParent();
if (parent == null || !Files.exists(path)) {
throw new IllegalArgumentException("配置文件不存在: " + configPath);
}
// 加载初始配置
loadConfig(path);
// 注册监控
parent.register(watchService,
StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("开始监控配置文件: " + configPath);
}
/**
* 添加配置变更监听器
*/
public void addListener(String configPath, ConfigChangeListener listener) {
listeners.put(configPath, listener);
}
/**
* 加载配置文件
*/
private void loadConfig(Path path) throws IOException {
Map<String, String> config = new ConcurrentHashMap<>();
Files.readAllLines(path, StandardCharsets.UTF_8).stream()
.filter(line -> !line.trim().startsWith("#") && line.contains("="))
.forEach(line -> {
String[] parts = line.split("=", 2);
config.put(parts[0].trim(), parts[1].trim());
});
String key = path.toString();
Map<String, String> oldConfig = configCache.put(key, config);
// 通知监听器
if (oldConfig != null) {
ConfigChangeListener listener = listeners.get(key);
if (listener != null) {
listener.onConfigChanged(key, config);
}
}
}
/**
* 启动监控
*/
public void start() {
executor.submit(() -> {
try {
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path filePath = (Path) key.watchable();
Path fullPath = filePath.resolve(filename);
// 检查是否是监控的配置文件
String pathStr = fullPath.toString();
if (configCache.containsKey(pathStr)) {
System.out.println("检测到配置文件变更: " + pathStr);
loadConfig(fullPath);
}
}
key.reset();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("监控已停止");
}
});
}
/**
* 停止监控
*/
public void stop() {
executor.shutdownNow();
try {
watchService.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用示例
public static void main(String[] args) throws IOException {
// 创建测试配置文件
Path configFile = Paths.get("/tmp/config.properties");
Files.write(configFile,
"name=test\nversion=1.0\n".getBytes(StandardCharsets.UTF_8));
// 创建监控器
ConfigWatcher watcher = new ConfigWatcher();
// 添加监听器
watcher.addListener(configFile.toString(), (path, config) -> {
System.out.println("配置已更新: " + path);
config.forEach((k, v) -> System.out.println(" " + k + " = " + v));
});
// 开始监控
watcher.watchConfig(configFile.toString());
watcher.start();
// 模拟配置修改
Thread.sleep(2000);
Files.write(configFile,
"name=test\nversion=2.0\nenabled=true\n".getBytes(StandardCharsets.UTF_8));
// 等待处理
Thread.sleep(2000);
watcher.stop();
}
}
文件过滤监控器
import java.io.IOException;
import java.nio.file.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
/**
* 带过滤条件的文件监控器
*/
public class FilteredWatcher {
private final WatchService watchService;
private final Set<Predicate<Path>> filters = new HashSet<>();
public FilteredWatcher() throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
}
/**
* 添加过滤器
*/
public void addFilter(Predicate<Path> filter) {
filters.add(filter);
}
/**
* 添加扩展名过滤
*/
public void addExtensionFilter(String... extensions) {
Set<String> extSet = new HashSet<>();
for (String ext : extensions) {
extSet.add(ext.toLowerCase());
}
filters.add(path -> {
String filename = path.getFileName().toString().toLowerCase();
return extSet.stream().anyMatch(filename::endsWith);
});
}
/**
* 注册目录
*/
public void register(Path dir) throws IOException {
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
System.out.println("监控目录: " + dir);
}
/**
* 监控循环
*/
public void watch() throws InterruptedException {
System.out.println("开始监控(带过滤器)...");
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path context = pathEvent.context();
Path dir = (Path) key.watchable();
Path fullPath = dir.resolve(context);
// 应用过滤器
boolean shouldProcess = filters.isEmpty() ||
filters.stream().allMatch(f -> f.test(fullPath));
if (shouldProcess) {
System.out.printf("%s: %s%n",
event.kind(), fullPath);
// 处理文件内容变化
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
processFile(fullPath);
}
}
}
key.reset();
}
}
private void processFile(Path file) {
// 处理文件内容
System.out.println(" - 处理文件: " + file);
try {
long size = Files.size(file);
System.out.println(" - 文件大小: " + size + " bytes");
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用示例
public static void main(String[] args) throws IOException, InterruptedException {
FilteredWatcher watcher = new FilteredWatcher();
// 只监控txt和log文件
watcher.addExtensionFilter("txt", "log");
// 或者使用自定义过滤器
// watcher.addFilter(path ->
// path.getFileName().toString().startsWith("temp_"));
// 注册目录
Path dir = Paths.get("/tmp/watch_filter");
if (!Files.exists(dir)) {
Files.createDirectories(dir);
}
watcher.register(dir);
// 模拟文件操作
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
Files.write(Paths.get("/tmp/watch_filter/test.txt"), "Hello".getBytes());
Thread.sleep(1000);
Files.write(Paths.get("/tmp/watch_filter/ignore.dat"), "Ignore".getBytes());
Thread.sleep(1000);
Files.write(Paths.get("/tmp/watch_filter/app.log"), "Log message".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
watcher.watch();
}
}
使用建议
-
性能考虑:WatchService是事件驱动的,但需要注意:
- 高频率修改事件可能导致性能问题
- 使用OVERFLOW事件处理事件丢失情况
- 考虑事件积压和线程池配置
-
可靠性:
- 确保正确调用
key.reset()以继续接收事件 - 处理无效的WatchKey(目录被删除等)
- 考虑使用异常处理和重试机制
- 确保正确调用
-
适用场景:
- 配置文件热更新
- 日志文件监控
- 上传文件自动处理
- 目录同步工具
这些案例覆盖了Java WatchService的主要使用场景,可以根据实际需求选择合适的实现。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播