Java监听文件内容变化需结合WatchService事件与内容哈希比对,或用Apache Commons IO轮询;须避免仅依赖lastModified()、处理原子替换、软链接及OVERFLOW等陷阱。

Java 中监听“文件内容的变化”不能靠单纯判断 lastModified() 或文件大小,因为很多编辑器(如 VS Code、IntelliJ)保存时采用“写临时文件 + 原子替换”策略——原文件被删、新文件被创建,此时内容变了,但触发的是 ENTRY_DELETE + ENTRY_CREATE,而非 ENTRY_MODIFY。真正可靠的方案需结合事件语义与内容比对。
用 WatchService 捕获变更事件并验证内容
WatchService 是 JDK 原生推荐方式,基于系统 inotify/ReadDirectoryChangesW,低开销、近实时。但它只通知“发生了什么”,不保证“内容是否真变”。所以正确做法是:
- 注册目标目录(注意:必须存在,且仅监听直接子项;若需递归,要遍历子目录逐个注册或用
Files.walkFileTree) - 监听
ENTRY_CREATE、ENTRY_MODIFY、ENTRY_DELETE三类事件 - 收到事件后,不是立刻 reload,而是计算当前文件的哈希值(如 SHA-256)或读取内容摘要,与上次缓存的哈希比对
- 仅当哈希不一致时,才认定“内容变化”,执行重加载逻辑
用 Apache Commons IO 的 FileAlterationMonitor(轮询式)
它本质是定时扫描(默认每 1–10 秒),适合对实时性要求不高、或运行在 WatchService 支持不佳环境(如某些 macOS 场景)的项目:
- 依赖
commons-io:2.7+,支持 JDK 8+ - 通过
FileAlterationObserver指定目录 + 文件过滤器(例如只监听.properties) - 继承
FileAlterationListenerAdaptor,覆盖onFileChange(File)方法,在其中读取并校验文件内容 - 优势是稳定、跨平台兼容好;缺点是延迟固定、有 CPU 轮询开销
避免常见误判的细节
光监听事件还不够,实际落地常踩这些坑:
立即学习“Java免费学习笔记(深入)”;
- 不要只信 lastModified():NFS、容器挂载、某些编辑器可能不更新时间戳,或同一秒内多次保存导致时间相同但内容不同
-
区分“文件被替换”和“文件被编辑”:原子替换场景下,
ENTRY_MODIFY可能完全不触发,必须靠CREATE/DELETE组合 + 内容比对来兜底 - 注意软链接与权限:注册路径若含符号链接,WatchService 可能跳过或失败;无读权限的子目录会导致递归注册中断
- 处理 OVERFLOW:表示内核事件队列溢出,事件已丢失,此时应强制全量校验所有监听文件
轻量封装建议
可定义一个简单接口统一行为:
public interface FileContentWatcher {
void watch(Path file, Runnable onContentChange);
void stop();
}
内部根据运行环境自动选择 WatchService(Linux/Windows)或轮询(macOS 兜底),并在回调前完成内容一致性验证。这样业务层只需关注“内容变了该做什么”,不用操心底层机制差异。


















