ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 自动重新加载属性的 Java `WatchService`示例 > 原文: [https://howtodoinjava.com/java7/auto-reload-of-configuration-when-any-change-happen/](https://howtodoinjava.com/java7/auto-reload-of-configuration-when-any-change-happen/) **每当配置文件**中发生任何更改时,都会自动刷新这些文件 – 这是大多数应用程序中常见的一个常见问题。 每个应用程序都有一些配置,预期该配置文件中的每次更改都会刷新。 解决该问题的过去方法包括使用`Thread`,它根据配置文件的*最后更新时间戳*定期轮询文件更改。 现在使用 Java 7,情况已经改变。 Java 7 引入了一项出色的特性:[**`WatchService`**](https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html)。 我将尽力为您解决上述问题。 这可能不是最好的实现,但是肯定会为您的解决方案提供一个很好的开始。 我敢打赌! ```java Table of Contents: 1) A brief overview of WatchService 2) Writing our configuration provider 3) Introducing configuration change listener 4) Testing our code 5) Key notes ``` ## 1\. Java `WatchService` API `WatchService`是 JDK 的内部服务,监视注册对象的更改。 这些注册的对象必定是[`Watchable`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/Watchable.html "Watchable interface")接口的实例。 在`WatchService`中注册`Watchable`实例时,我们需要指定我们感兴趣的变更事件的类型。 到目前为止,有四种类型的事件: 1. [`ENTRY_CREATE`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/StandardWatchEventKinds.html#ENTRY_CREATE "ENTRY_CREATE") , 2. [`ENTRY_DELETE`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/StandardWatchEventKinds.html#ENTRY_DELETE "ENTRY_DELETE") , 3. [`ENTRY_MODIFY`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/StandardWatchEventKinds.html#ENTRY_MODIFY "ENTRY_MODIFY") 和 4. [`OVERFLOW`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/StandardWatchEventKinds.html#OVERFLOW "OVERFLOW")。 您可以在提供的链接中了解这些事件。 `WatchService`接口扩展了[`Closeable`](https://docs.oracle.com/javase/7/docs/api/java/io/Closeable.html "Closable interface")接口,表示可以根据需要关闭服务。 通常,应该使用 JVM 提供的关闭挂钩来完成。 ## 2\. 应用程序配置供应器 配置供应器只是用于包装[`java.util.Properties`](https://docs.oracle.com/javase/6/docs/api/java/util/Properties.html "java util Properties")实例中的属性集的包装器。 它还提供了使用**键**来获取已配置属性的方法。 ```java package testWatchService; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ApplicationConfiguration { private final static ApplicationConfiguration INSTANCE = new ApplicationConfiguration(); public static ApplicationConfiguration getInstance() { return INSTANCE; } private static Properties configuration = new Properties(); private static Properties getConfiguration() { return configuration; } public void initilize(final String file) { InputStream in = null; try { in = new FileInputStream(new File(file)); configuration.load(in); } catch (IOException e) { e.printStackTrace(); } } public String getConfiguration(final String key) { return (String) getConfiguration().get(key); } public String getConfigurationWithDefaultValue(final String key, final String defaultValue) { return (String) getConfiguration().getProperty(key, defaultValue); } } ``` ## 3\. 配置更改监听器 – 文件监视器 现在,当我们有了配置属性的内存中缓存的基本包装器时,我们需要一种机制,只要存储在文件系统中的配置文件发生更改,就可以在运行时重新加载此缓存。 我已经写了一个示例工作代码来为您提供帮助: ```java package testWatchService; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; public class ConfigurationChangeListner implements Runnable { private String configFileName = null; private String fullFilePath = null; public ConfigurationChangeListner(final String filePath) { this.fullFilePath = filePath; } public void run() { try { register(this.fullFilePath); } catch (IOException e) { e.printStackTrace(); } } private void register(final String file) throws IOException { final int lastIndex = file.lastIndexOf("/"); String dirPath = file.substring(0, lastIndex + 1); String fileName = file.substring(lastIndex + 1, file.length()); this.configFileName = fileName; configurationChanged(file); startWatcher(dirPath, fileName); } private void startWatcher(String dirPath, String file) throws IOException { final WatchService watchService = FileSystems.getDefault() .newWatchService(); Path path = Paths.get(dirPath); path.register(watchService, ENTRY_MODIFY); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { watchService.close(); } catch (IOException e) { e.printStackTrace(); } } }); WatchKey key = null; while (true) { try { key = watchService.take(); for (WatchEvent<?> event : key.pollEvents()) { if (event.context().toString().equals(configFileName)) { configurationChanged(dirPath + file); } } boolean reset = key.reset(); if (!reset) { System.out.println("Could not reset the watch key."); break; } } catch (Exception e) { System.out.println("InterruptedException: " + e.getMessage()); } } } public void configurationChanged(final String file) { System.out.println("Refreshing the configuration."); ApplicationConfiguration.getInstance().initilize(file); } } ``` 上面的类是使用线程创建的,该线程将使用`WatchService`监听配置属性文件的更改。 一旦检测到文件中的任何修改,它便会刷新配置中的内存缓存。 上述监听器的构造器仅采用一个参数,即受监视的配置文件的标准路径。 在文件系统中更改配置文件时,会立即通知`Listener`类。 然后,此监听器类调用`ApplicationConfiguration.getInstance().initilize(file);`以重新加载到内存缓存中。 ## 4\. 测试我们的代码 现在,当我们准备好类时,我们将对其进行测试。 首先,将`test.properties`文件及其以下内容存储在`'C:/Lokesh/temp'`文件夹中。 ```java TEST_KEY=TEST_VALUE ``` 现在,让我们使用以下代码测试以上类。 ```java package testWatchService; public class ConfigChangeTest { private static final String FILE_PATH = "C:/Lokesh/temp/test.properties"; public static void main(String[] args) { ConfigurationChangeListner listner = new ConfigurationChangeListner( FILE_PATH); try { new Thread(listner).start(); while (true) { Thread.sleep(2000l); System.out.println(ApplicationConfiguration.getInstance() .getConfiguration("TEST_KEY")); } } catch (Exception e) { e.printStackTrace(); } } } //Output of the above program (Change the TEST_VALUE to TEST_VALUE1 and TEST_VALUE2 using any file editor and save). Refreshing the configuration. TEST_VALUE TEST_VALUE TEST_VALUE Refreshing the configuration. TEST_VALUE1 Refreshing the configuration. TEST_VALUE2 ``` 以上输出表明,每次我们对属性文件进行任何更改时,刷新的属性都会刷新,并且可以使用新的属性值。 到目前为止,已经做好了! ## 5\. 重要说明 1. 如果在新项目中使用 Java 7,并且没有在使用老式方法重新加载属性,则说明操作不正确。 2. `WatchService`提供了两个方法[`take()`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html#take%28%29 "Watch Service take method")和[`poll()`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html#take%28%29 "watch Service poll method")。当`take()`方法等待下一次更改发生并被阻止之前,`poll()`立即检查更改事件。 如果上次`poll()`调用没有任何变化,它将返回 `null`。 `poll()`方法不会阻止执行,因此应在具有一些睡眠时间的线程中调用。 将我的问题/建议放在评论区。 学习愉快!