這篇文章將介紹如何使用Guava EventBus將更改發布到Java 7 WatchService檢測到的目錄或子目錄中。 Guava EventBus是向應用程序添加發布/訂閱通信的好方法。 Java 7 java.nio.file軟件包中新增的WatchService用于監視目錄中的更改。 由于EventBus和WatchService已在以前的文章中介紹過,因此我們在這里不會深入介紹這些主題。 有關更多信息,鼓勵讀者查看EventBus和WatchService帖子。 [注意:為清楚起見,帖子于2012年2月28日更新。]
為什么使用EventBus
將EventBus與WatchService一起使用的主要原因有兩個。
- 我們不希望輪詢事件,而是希望接收異步通知。
- 處理事件后,需要調用WatchKey.reset方法以使所有新更改都可以排隊。 盡管WatchKey對象是線程安全的,但重要的是僅在所有線程完成處理事件之后才調用reset方法,這會導致一些協調麻煩。 使用單個線程處理事件,調用reset方法,然后通過EventBus發布更改,消除了此問題。
我們實現這一目標的計劃很簡單,將涉及以下步驟:
- 實例化WatchService的實例。
- 從給定的Path對象開始遞歸注冊每個目錄。
- 將事件從WatchService隊列中移出,然后處理并發布這些事件。
- 啟動一個單獨的線程以使事件脫離隊列并發布。
下面的代碼示例是DirectoryEventWatcherImpl類中更相關的重點,它將完成所有這些工作。
在WatchService中注冊目錄
在添加或刪除子目錄時將生成事件,而在監視目錄的子目錄內進行的任何更改均不會。 我們將通過遞歸遍歷所有子目錄(通過Files.walkFileTree方法)并使用WatchService對象(在此示例中先前定義)注冊每個子目錄來對此進行補償:
private void registerDirectories() throws IOException {Files.walkFileTree(startPath, new WatchServiceRegisteringVisitor());
}private class WatchServiceRegisteringVisitor extends SimpleFileVisitor<Path>{@Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {dir.register(watchService,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);return FileVisitResult.CONTINUE;}
}
在第2行,Files.walkFileTree方法使用在第5行定義的WatchServiceRegisteringVisitor類向WatchService注冊每個目錄。 所注冊的事件是文件/目錄的創建,文件/目錄的刪除或文件的更新。
發布事件
下一步是創建一個FutureTask,它將執行檢查隊列和發布事件的工作。
private void createWatchTask() {watchTask = new FutureTask<>(new Callable<Integer>() {private int totalEventCount;@Overridepublic Integer call() throws Exception {while (keepWatching) {WatchKey watchKey = watchService.poll(10, TimeUnit.SECONDS);if (watchKey != null) {List<WatchEvent<?>> events = watchKey.pollEvents();Path watched = (Path) watchKey.watchable();PathEvents pathEvents = new PathEvents(watchKey.isValid(), watched);for (WatchEvent event : events) {pathEvents.add(new PathEvent((Path) event.context(), event.kind()));totalEventCount++;}watchKey.reset();eventBus.post(pathEvents);}}return totalEventCount;}});}private void startWatching() {new Thread(watchTask).start();
}
在第7行,我們每10秒檢查一次WatchService是否有排隊事件。 當返回有效的WatchKey時,第一步是檢索事件(第9行),然后獲取發生事件的目錄(第10行)。 在第11行,將創建一個PathEvents對象,該對象將一個布爾值和受監視的目錄用作構造函數參數。 第12至15行使用目標Path和事件類型作為創建PathEvent對象的參數遍歷第9行檢索到的事件。 在第16行調用WatchKey.reset方法,將WatchKey狀態設置回ready,使其有資格接收新事件并將其放回到隊列中。 最后,在第17行,EventBus將PathEvents對象發布給所有訂閱者。 重要的是在這里注意PathEvents和PathEvent類是不可變的。 從Callable返回的totalEventCount永遠不會在API中公開,而是用于測試目的。 第25行的startWatching方法啟動線程以運行上面定義的監視/發布任務。
結論
通過將WatchService與Guava EventBus配對,我們可以在單個線程中管理WatchKey并處理事件,并以異步方式通知任意數量的訂閱者該事件。 希望讀者發現此示例有用。 一如既往地歡迎提出意見和建議。
資源資源
- 這篇文章的源代碼和單元測試
- EventBus API
- WatchService API
- WatchService上的上一篇文章 。
- EventBus上的上一篇文章
參考: 事件編程示例:來自JCG合作伙伴 Bill Bejeck的Google Guava EventBus和Java 7 WatchService,來自“ 隨機思考編碼”博客。
翻譯自: https://www.javacodegeeks.com/2012/12/google-guava-eventbus-and-java-7-watchservice-for-event-programming.html