推荐使用jdk 7引入的java.nio.file.watchservice,它基于操作系统原生事件(如inotify/kqueue),轻量高效、跨平台;但默认仅监听注册路径的直接子项,需手动递归注册子目录并动态管理监听路径集合,同时注意系统限制、事件去抖与可靠性补救措施。

Java 中监控文件夹变动,推荐使用 java.nio.file.WatchService,它是 JDK 7 引入的轻量、跨平台、基于操作系统事件(如 inotify、kqueue、Windows API)的原生方案,比轮询高效得多。
用 WatchService 监控目录变化
WatchService 本身不递归监听子目录,只监控注册路径的直接子项。需手动处理目录创建/删除事件来动态增删监听。
- 创建 WatchService 实例:
FileSystems.getDefault().newWatchService() - 对目标路径调用
register(),指定监听事件类型:ENTRY_CREATE、ENTRY_DELETE、ENTRY_MODIFY - 通过
watcher.take()或watcher.poll(1, TimeUnit.SECONDS)获取事件,阻塞或非阻塞均可 - 每个事件包含
kind()(什么操作)、context()(文件名)、count()(重复次数,通常为 1)
注意递归监听子目录
WatchService 默认不递归。若需监控整个目录树,必须在收到 ENTRY_CREATE 事件且 context 是目录时,立即对该子目录再次 register,并维护监听路径集合防止重复注册。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 建议封装一个
registerAll(Path start)方法,用 Files.walkFileTree 预注册,再监听新增目录 - 遇到 ENTRY_DELETE 事件时,从监听集合中移除对应路径(注意:某些系统可能不触发已删除目录的注销)
- 避免监听符号链接目标,注册前可用
Files.isSymbolicLink(path)过滤
常见问题与避坑点
WatchService 行为受底层 OS 限制,实际使用中容易忽略这些细节:
- Linux 上 inotify 有句柄数限制(
/proc/sys/fs/inotify/max_user_watches),大量目录监听可能抛java.io.IOException: No space left on device,需适当调大 - Windows 对短文件名(8.3 格式)或重命名操作可能合并多个事件,比如先 DELETE 再 CREATE,逻辑上应视为 RENAME
- MODIFY 事件可能高频触发(如编辑器保存时写临时文件再替换),建议加简单去抖:100ms 内相同文件的多次 MODIFY 合并处理
- WatchService 不保证事件顺序,也不保证所有事件必达(极端负载下可能丢事件),关键业务需配合定期快照校验
替代方案参考
如果项目已用 Spring,Spring Framework 5.3+ 提供了 FileSystemWatcher 封装,自动处理递归、去抖、异常恢复;Apache Commons IO 的 FileMonitor 基于轮询,适合极简场景或兼容老 JDK,但不推荐生产高频监控。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










