WatchService 不监听变量变动,只监听文件系统事件;热重载需在收到创建/修改/删除通知后,主动解析文件并更新内存状态。

WatchService 本身不监听“变量变动”,它监听的是文件系统中文件或目录的创建、修改、删除等事件。所谓“热重载”(如配置更新、代码变更后自动生效),需在收到 WatchService 通知后,由你主动解析文件内容、触发重新加载逻辑。关键不在监听“变量”,而在监听承载变量的文件(如 application.yml、config.properties 或自定义脚本)。
核心思路:文件变更 → 解析内容 → 更新内存状态
Java 的 WatchService 是基于操作系统底层 inotify(Linux)、kqueue(macOS)或 ReadDirectoryChangesW(Windows)的轻量封装,仅负责“有文件被改了”这一事实,不关心改了什么、是否是变量、值变没变。因此必须配套实现:
- 将配置/规则/脚本等外部化为可读文件(如 JSON/YAML/Properties)
- 建立内存中的运行时状态(如
Map或类型化配置类) - 监听指定路径,当
ENTRY_MODIFY事件触发时,安全地重新读取并解析该文件 - 原子性地替换旧配置引用(避免读写竞争),必要时触发回调(如刷新缓存、重启线程池)
基础监听模板(支持单文件或整个目录)
以下是一个最小可用示例,监听 ./config/ 目录下所有 .yaml 文件的修改事件:
Path configDir = Paths.get("./config");
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
configDir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
while (true) {
WatchKey key;
try {
key = watcher.take(); // 阻塞等待事件
} catch (InterruptedException e) {
break;
}
for (WatchEvent> event : key.pollEvents()) {
WatchEvent.Kind> kind = event.kind();
Path fileName = (Path) event.context();
if (kind == StandardWatchEventKinds.ENTRY_MODIFY &&
fileName.toString().endsWith(".yaml")) {
Path fullPath = configDir.resolve(fileName);
reloadConfig(fullPath); // 自定义重载方法
}
}
if (!key.reset()) break; // 事件处理完必须 reset,否则不再接收新事件
}
}
注意:WatchService 不递归监听子目录;若需监听多层,需对每个子目录单独注册,或使用第三方库(如 Apache Commons IO 的 FileAlterationObserver)。
安全重载的关键细节
直接在监听线程里 reload 可能引发并发问题(比如其他线程正在用旧配置)。推荐做法:
- 用
AtomicReference存储当前配置实例,reload()中解析新内容后调用set(newConfig) - 避免在 reload 过程中阻塞 WatchService 线程——解析大文件或远程拉取时,应异步执行(如提交到
ExecutorService) - 校验文件完整性:读取前检查文件是否正在被写入(例如通过文件长度突变、临时文件命名约定、或加锁文件
.writing) - 记录日志:打印变更文件名、解析是否成功、新旧值差异(调试用),但避免打印敏感配置项
进阶建议:用现成方案降低风险
生产环境不建议从零手写 WatchService 热重载,原因包括:
- 跨平台行为差异(如 macOS 对符号链接、NFS 的支持较弱)
- 事件丢失可能(高频修改时多个事件合并为一个
ENTRY_MODIFY) - 资源泄漏(未 close WatchService / WatchKey)或线程泄漏(未正确 shutdown Executor)
更稳妥的选择:
- Spring Boot 的
@ConfigurationPropertiesRefreshScope+spring-boot-devtools(开发期) - Spring Cloud Config + Bus(配合 RabbitMQ/Kafka 实现集群广播)
- Apache Commons Configuration2 的
FileBasedConfigurationBuilder+ReloadingController - 小型项目可用 io.watchdog:watchdog(轻量 Kotlin/Java 库,自动去抖、异常隔离)
自己封装 WatchService 完全可行,但务必加上超时重试、事件队列缓冲、配置版本比对等防护逻辑。










