本文目录导读:

- 使用Java NIO的WatchService(推荐)
- 增强版:递归监控子目录
- 使用Apache Commons IO
- 使用Java的File.lastModified()轮询(基础方法)
- 实际应用:配置文件监控器
- 性能对比和选择建议
我来介绍几种Java实现文件监控的方法,从基础到高级:
使用Java NIO的WatchService(推荐)
这是Java 7+官方推荐的文件监控方式:
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
public class FileWatcherExample {
public static void main(String[] args) throws IOException, InterruptedException {
// 监控的目录
Path dir = Paths.get("/path/to/monitor");
// 创建WatchService
WatchService watchService = FileSystems.getDefault().newWatchService();
// 注册监控事件
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("开始监控目录: " + dir.toAbsolutePath());
// 循环处理事件
while (true) {
WatchKey key = watchService.take(); // 阻塞等待
List<WatchEvent<?>> events = key.pollEvents();
for (WatchEvent<?> event : events) {
WatchEvent.Kind<?> kind = event.kind();
Path filename = (Path) event.context();
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("文件创建: " + filename);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("文件删除: " + filename);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("文件修改: " + filename);
}
}
// 重置WatchKey
boolean valid = key.reset();
if (!valid) {
break;
}
}
watchService.close();
}
}
增强版:递归监控子目录
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
public class RecursiveFileWatcher {
private final WatchService watchService;
private final Map<WatchKey, Path> keyPathMap = new HashMap<>();
public RecursiveFileWatcher() throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
}
// 递归注册所有子目录
private void registerDirectory(Path dir) throws IOException {
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
WatchKey key = dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
keyPathMap.put(key, dir);
System.out.println("监控目录: " + dir);
return FileVisitResult.CONTINUE;
}
});
}
public void startMonitoring(Path rootDir) throws IOException, InterruptedException {
registerDirectory(rootDir);
while (true) {
WatchKey key = watchService.take();
Path dir = keyPathMap.get(key);
if (dir == null) continue;
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path filename = (Path) event.context();
Path fullPath = dir.resolve(filename);
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("创建: " + fullPath);
// 如果是新创建的目录,也要注册监控
if (Files.isDirectory(fullPath)) {
registerDirectory(fullPath);
}
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("删除: " + fullPath);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("修改: " + fullPath);
}
}
if (!key.reset()) {
keyPathMap.remove(key);
if (keyPathMap.isEmpty()) break;
}
}
}
public static void main(String[] args) throws Exception {
RecursiveFileWatcher watcher = new RecursiveFileWatcher();
watcher.startMonitoring(Paths.get("/path/to/monitor"));
}
}
使用Apache Commons IO
需要添加依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.io.File;
public class CommonsIOFileWatcher {
public static void main(String[] args) throws Exception {
// 监控目录
File directory = new File("/path/to/monitor");
// 创建文件观察器
FileAlterationObserver observer = new FileAlterationObserver(directory);
// 添加事件监听器
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println("文件创建: " + file.getAbsolutePath());
}
@Override
public void onFileDelete(File file) {
System.out.println("文件删除: " + file.getAbsolutePath());
}
@Override
public void onFileChange(File file) {
System.out.println("文件修改: " + file.getAbsolutePath());
}
@Override
public void onDirectoryCreate(File dir) {
System.out.println("目录创建: " + dir.getAbsolutePath());
}
@Override
public void onDirectoryDelete(File dir) {
System.out.println("目录删除: " + dir.getAbsolutePath());
}
});
// 创建监控器,设置检查间隔(毫秒)
FileAlterationMonitor monitor = new FileAlterationMonitor(1000, observer);
// 启动监控
monitor.start();
System.out.println("开始监控...");
// 运行一段时间后停止
Thread.sleep(60000);
monitor.stop();
}
}
使用Java的File.lastModified()轮询(基础方法)
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class PollingFileWatcher {
private final Map<String, Long> fileLastModifiedMap = new HashMap<>();
private volatile boolean running = true;
public void startMonitoring(String directoryPath, long interval) {
File directory = new File(directoryPath);
new Thread(() -> {
while (running) {
try {
checkDirectory(directory);
Thread.sleep(interval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "File-Watcher").start();
}
private void checkDirectory(File directory) {
File[] files = directory.listFiles();
if (files == null) return;
for (File file : files) {
String path = file.getAbsolutePath();
long lastModified = file.lastModified();
Long previousModified = fileLastModifiedMap.get(path);
if (previousModified == null) {
// 新文件
System.out.println("新文件: " + path);
fileLastModifiedMap.put(path, lastModified);
} else if (previousModified != lastModified) {
// 文件被修改
System.out.println("文件修改: " + path);
fileLastModifiedMap.put(path, lastModified);
}
}
// 检查是否有文件被删除
fileLastModifiedMap.keySet().removeIf(path -> !new File(path).exists());
}
public void stop() {
this.running = false;
}
public static void main(String[] args) throws InterruptedException {
PollingFileWatcher watcher = new PollingFileWatcher();
watcher.startMonitoring("/path/to/monitor", 1000); // 每秒检查一次
Thread.sleep(60000);
watcher.stop();
}
}
实际应用:配置文件监控器
import java.io.IOException;
import java.nio.file.*;
import java.util.function.Consumer;
public class ConfigFileWatcher {
private final Path configPath;
private final Consumer<Path> onFileChange;
private Thread watchThread;
public ConfigFileWatcher(String configFilePath, Consumer<Path> onFileChange) {
this.configPath = Paths.get(configFilePath).toAbsolutePath();
this.onFileChange = onFileChange;
}
public void start() throws IOException {
Path parentDir = configPath.getParent();
if (!Files.exists(parentDir)) {
Files.createDirectories(parentDir);
}
watchThread = new Thread(() -> {
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
parentDir.register(watchService,
StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("开始监控配置文件: " + configPath);
while (!Thread.currentThread().isInterrupted()) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
Path changedFile = (Path) event.context();
Path fullPath = parentDir.resolve(changedFile);
if (fullPath.equals(configPath)) {
System.out.println("配置文件已修改: " + configPath);
onFileChange.accept(configPath);
}
}
if (!key.reset()) break;
}
watchService.close();
} catch (IOException | InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Config-Watcher");
watchThread.setDaemon(true);
watchThread.start();
}
public void stop() {
if (watchThread != null) {
watchThread.interrupt();
}
}
// 使用示例
public static void main(String[] args) throws IOException {
ConfigFileWatcher watcher = new ConfigFileWatcher(
"/etc/myapp/config.properties",
path -> {
System.out.println("重新加载配置...");
// 重新加载配置文件的逻辑
}
);
watcher.start();
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(watcher::stop));
}
}
性能对比和选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| WatchService | 实时性高,资源消耗低 | 部分系统支持有限 | 大多数文件监控需求 |
| Apache Commons IO | 功能完善,易用性好 | 需要外部依赖 | 需要高级功能的项目 |
| 轮询方式 | 简单直接,兼容性好 | 延迟高,资源消耗大 | 简单场景或旧系统 |
最佳实践建议:
- 首选使用Java NIO的WatchService
- 对于生产环境,考虑使用成熟的框架如Apache Commons IO或JNotify
- 监控配置文件的变更是最常见的应用场景
- 注意处理WatchEvent.OVERFLOW事件,表示可能丢失事件
这些示例涵盖了文件监控的主要实现方式,你可以根据具体需求选择合适的方案。