java 如何實現文件變動的監聽


獲取修改時間

long lastTime = file.lastModified();

 

原文鏈接:https://blog.csdn.net/liuyueyi25/article/details/79292971

現在的問題時,我需要在這個文件的內容發生變動時,應用可以感知這種變動,並重新加載文件內容,更新應用內部緩存

一個最容易想到的方法,就是輪詢,判斷文件是否發生修改,如果修改了,則重新加載,並刷新內存,所以主要需要關心的問題如下:

如何輪詢?
如何判斷文件是否修改?
配置異常,會不會導致服務不可用?(即容錯,這個與本次主題關聯不大,但又比較重要…)
II. 設計與實現
問題抽象出來之后,對應的解決方案就比較清晰了

如何輪詢 ? –》 定時器 Timer, ScheduledExecutorService 都可以實現
如何判斷文件修改? –》根據 java.io.File#lastModified 獲取文件的上次修改時間,比對即可

進階方法

<dependency>

  <groupId>commons-io</groupId>

 <artifactId>commons-io</artifactId>

 <version>2.6</version>

</dependency>

主要是借助這個工具中的 FileAlterationObserver, FileAlterationListener, FileAlterationMonitor 三個類來實現相關的需求場景了,當然使用也算是很簡單了,以至於都不太清楚可以再怎么去說明了,直接看下面從我的一個開源項目quick-alarm中拷貝出來的代碼

public class PropertiesConfListenerHelper {

    public static boolean registerConfChangeListener(File file, Function<File, Map<String, AlarmConfig>> func) {
        try {
            // 輪詢間隔 5 秒
            long interval = TimeUnit.SECONDS.toMillis(5);


            // 因為監聽是以目錄為單位進行的,所以這里直接獲取文件的根目錄
            File dir = file.getParentFile();

            // 創建一個文件觀察器用於過濾
            FileAlterationObserver observer = new FileAlterationObserver(dir,
                    FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
                            FileFilterUtils.nameFileFilter(file.getName())));

            //設置文件變化監聽器
            observer.addListener(new MyFileListener(func));
            FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
            monitor.start();

            return true;
        } catch (Exception e) {
            log.error("register properties change listener error! e:{}", e);
            return false;
        }
    }


    static final class MyFileListener extends FileAlterationListenerAdaptor {

        private Function<File, Map<String, AlarmConfig>> func;

        public MyFileListener(Function<File, Map<String, AlarmConfig>> func) {
            this.func = func;
        }

        @Override
        public void onFileChange(File file) {
            Map<String, AlarmConfig> ans = func.apply(file); // 如果加載失敗,打印一條日志
            log.warn("PropertiesConfig changed! reload ans: {}", ans);
        }
    }
}

針對上面的實現,簡單說明幾點:

這個文件監聽,是以目錄為根源,然后可以設置過濾器,來實現對應文件變動的監聽
如上面registerConfChangeListener方法,傳入的file是具體的配置文件,因此構建參數的時候,撈出了目錄,撈出了文件名作為過濾
第二參數是jdk8語法,其中為具體的讀取配置文件內容,並映射為對應的實體對象
一個問題,如果 func方法執行時,也拋出了異常,會怎樣?

實際測試表現結果和上面一樣,拋出異常之后,依然跪,所以依然得注意,不要跑異常

那么簡單來看一下上面的實現邏輯,直接扣出核心模塊

public void run() {
    while(true) {
        if(this.running) {
            Iterator var1 = this.observers.iterator();

            while(var1.hasNext()) {
                FileAlterationObserver observer = (FileAlterationObserver)var1.next();
                observer.checkAndNotify();
            }

            if(this.running) {
                try {
                    Thread.sleep(this.interval);
                } catch (InterruptedException var3) {
                    ;
                }
                continue;
            }
        }

        return;
    }
}

 

IV. 小結
使用Java來實現配置文件變動的監聽,主要涉及到的就是兩個點

如何輪詢: 定時器(Timer, ScheduledExecutorService), 線程死循環+sleep
文件修改: File#lastModified
整體來說,這個實現還是比較簡單的,無論是自定義實現,還是依賴 commos-io來做,都沒太大的技術成本,但是需要注意的一點是:

千萬不要在定時任務 or 文件變動的回調方法中拋出異常!!!
為了避免上面這個情況,一個可以做的實現是借助EventBus的異步消息通知來實現,當文件變動之后,發送一個消息即可,然后在具體的重新加載文件內容的方法上,添加一個 @Subscribe注解即可,這樣既實現了解耦,也避免了異常導致的服務異常 (如果對這個實現有興趣的可以評論說明)
————————————————
版權聲明:本文為CSDN博主「一灰灰blog」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/liuyueyi25/article/details/79292971


————————————————
版權聲明:本文為CSDN博主「一灰灰blog」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/liuyueyi25/article/details/79292971

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM