細數Java項目中用過的配置文件(properties篇)

来源:https://www.cnblogs.com/socoool/archive/2020/04/12/12683501.html
-Advertisement-
Play Games

靈魂拷問:在不重啟服務的前提下,如何讓配置修改生效的呢?有什麼奇技淫巧嗎? 靈魂拷問:在 Java 項目中,總能看到以 .properties 為尾碼的文件蹤影,這類配置文件是怎麼載入的呢? 項目研發過程中,總會遇到一些經常改變的參數,比如要連接的資料庫的連接地址、名稱、用戶名、密碼;再比如訪問三方 ...


靈魂拷問:在不重啟服務的前提下,如何讓配置修改生效的呢?有什麼奇技淫巧嗎?

靈魂拷問:在 Java 項目中,總能看到以 .properties 為尾碼的文件蹤影,這類配置文件是怎麼載入的呢?

項目研發過程中,總會遇到一些經常改變的參數,比如要連接的資料庫的連接地址、名稱、用戶名、密碼;再比如訪問三方服務的 URL 等等。考慮到程式的通用性,這些參數往往不能直接寫死在程式里,通常藉助配置文件來優雅處理。

 在 Java 項目中,properties 文件當屬使用較簡單一類,不過雖然簡單,還是要好好說說項目中都是怎麼使用的,嘗試通過源碼解讀,讓你真正懂它,並帶你深刻體會 Java 中重載的意義。

 

1. 雖說簡單,格式還是要看一看。

相比上次談及的 ini 配置文件,properties 文件格式沒有 Section (節)的概念,反而是簡單了不少。

 

 

 

 上圖是一個 jdbc 連接所需要的配置,其中以 # 開始的每一行是註釋信息,而以等號分割的每行配置,就是常說的鍵-值對,等號左邊的為 key(代碼中的變數),等號右邊的為 value(是依據實際場景而配置的值)。

 

2. 雖說簡單,Java 源碼還是去要看看。

在 Java 中提供了 java.util.Properties 類,主要用於對配置文件的讀寫操作。

 

 

一圖掌握血緣關係,很顯然 Properties 繼承自 Hashtable,歸根結底是個 Map,而 Properties 最特殊的地方,就是它的鍵和值都是字元串類型。

 

從全局瞭解梗概,然後走進 JDK 源碼,按照思路,步步去深入。

 

首先,要看看寫好的配置文件是怎麼載入的?

 

 

源碼很清晰,提供字元流 Reader、位元組流 InputStream兩種方式載入配置文件(方法重載的目的:讓使用者更方便),再深入去看最終會調用 Hashtable 的 put(key, value) 來設置鍵值對,最終完成配置文件中的載入。

 

 

然後,要看看怎麼根據 key 獲得對應的 value(放進去了,還要考慮拿出來)?

 

 

源碼很清晰,通過參數 key 獲得對應的 value,考慮到使用者的方便,對 getProperty 方法進行了重載,其中標註 2 的方法,當根據 key 獲得值是 null 時,會返回一個調用方法時傳入的預設值(很多場景下,確實很有用)。

 

知道了怎麼載入配置文件,知道了怎麼獲取 key 對應的值,按照常理說,項目中已經夠用了,但是有些時候項目啟動後,還真需要再額外設置一下參數的值,不過沒關係,因為 Java 已經想到了這一點,對外提供了 setProperty 的方法,讓額外設置參數成為可能。

 

 

若能在項目研發中,熟練使用上面提到的這些 API,已經足矣。既然打開了源碼,索性把雜七雜八的都提提,說不定某些 API 也能解決你碰到的其它場景的問題呢。

 

 

Properties 類不僅提供了 load 進行載入配置,而且還提供了把鍵值對寫到文件中的能力。如上面源碼所示,考慮到使用者的方便,對 store 方法進行重載,提供了面向位元組流 OutputStream、字元流 Writer 兩種方式的 store。

 

 

如上面源碼所示,Properties 除了提供對常規配置文件讀寫能力支撐,對 xml 配置文件載入、寫入也提供了支撐。

 

 

如上圖源碼所示,Properties 類提供了重載的 list 方法,為了方便調試,可以把鍵值對列表給整齊的列印出來。

 

3. 雖說簡單,不能賦予實踐一切都是扯淡。

場景一:在 APM 性能監控時,獲取 Java 應用畫像信息常用 API。

import java.util.Properties;

/**
 * @author 一猿小講
 */
public class JVMDetails {
    public static void main(String[] args) {
        // 獲取系統信息(JDK信息、Java虛擬機信息、Java提供商信息、當前電腦用戶信息)
        Properties properties = System.getProperties();
        // 把系統信息列印一下
        properties.list(System.out);
    }
}

程式跑起來,部分輸出截圖示意如下。

 

 

 

場景二:業務開發中,讓配置替代硬編碼,並考慮配置更新時,程式能夠讀到最新的值。

import java.io.*;
import java.util.Hashtable;
import java.util.Properties;

/**
 * 配置文件工具類
 * 1. 支持載入 .properties文件、.ini文件
 * 2. 支持配置文件更新
 * @author 一猿小講
 */
public class PropertiesUtil {

    // private static final Log4j LOG =  .....;

    private static Hashtable<String, PropCache> propCache = new Hashtable<String, PropCache>();

    public static String getString(String propFile, String key) {
        return getString(propFile, key, null);
    }

    public static String getString(String propFile, String key, String defaultValue) {
        if ((!propFile.endsWith(".properties")) && (!propFile.endsWith(".ini"))) {
            propFile = propFile + ".properties";
        }
        PropCache prop = propCache.get(propFile);
        if (prop == null) {
            try {
                prop = new PropCache(propFile);
            } catch (IOException e) {
                // LOG.warn(e);
                System.out.println(String.format("讀取 %s 出現異常%s", propFile,e));
                return defaultValue;
            }
            propCache.put(propFile, prop);
        }
        String value = prop.getProperty(key, defaultValue);
        if (value != null) {
            value = value.trim();
        }
        return value;
    }

    public static void setString(String propFile, String key, String value)
            throws IOException {
        if ((!propFile.endsWith(".properties")) && (!propFile.endsWith(".ini"))) {
            propFile = propFile + ".properties";
        }
        File file = new File(propFile);
        Properties prop = new Properties();
        try {
            FileInputStream fis = new FileInputStream(file);
            prop.load(fis);
            fis.close();
        } catch (FileNotFoundException e) {
            // LOG.warn(e);
            System.out.println(String.format("文件 %s 不存在", propFile));
        }
        FileOutputStream fos = new FileOutputStream(file);
        prop.put(key, value);
        prop.store(fos, null);
        fos.close();
        PropCache localPropCache = propCache.get(propFile);
        if (localPropCache != null) {
            localPropCache.reload();
        }
    }

    private static class PropCache {

        private String fileName;
        private long lastLoad;
        private Properties prop;

        public PropCache(String propFileName) throws IOException {
            File file = new File(propFileName);
            FileInputStream fis = new FileInputStream(file);
            this.prop = new Properties();
            this.prop.load(fis);
            fis.close();
            this.fileName = file.getAbsolutePath();
            this.lastLoad = file.lastModified();
        }

        public String getProperty(String key, String defaultValue) {
            File file = new File(this.fileName);
            if (this.lastLoad < file.lastModified()) {
                reload();
            }
            return this.prop.getProperty(key, defaultValue);
        }

        public void reload() {
            File file = new File(this.fileName);
            try {
                Properties prop = new Properties();
                FileInputStream fis = new FileInputStream(file);
                prop.load(fis);
                fis.close();
                this.prop.clear();
                this.prop.putAll(prop);
                this.lastLoad = file.lastModified();
            } catch (IOException e) {
                // PropertiesUtil.LOG.warn(e);
                System.out.println(String.format("文件 %s 重新載入出現異常%s", this.fileName, e));
            }
            // PropertiesUtil.LOG.all(new Object[]{this.fileName, " reloaded."});
            System.out.println(String.format("文件 %s 重新載入完畢", this.fileName));
        }
    }
}

藉助開篇提到的 jdbc 配置文件,進行驗證。嘗試獲取資料庫類型,預設配置為 db2,中途修改參數的值為 mysql,看看效果如何?

/**
 * 測試類
 * @author 一猿小講
 */
public class M {
    public static void main(String[] args) {
        while(true) {
            System.out.println(PropertiesUtil.getString("db", "jdbc.type"));
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
            }
        }
    }
}

程式跑起來,效果還是讓人很滿意。

 

 

 

4. 雖說簡單,洋洋灑灑分享一大篇。

有關配置文件的分享網上有很多,而我們的分享卻顯得不太一樣。

我們的初衷是:結合實際項目及源碼,說說這些年用過的那些有關配置的奇技淫巧,幫你提高研發能力(那怕是提高一丟丟,就算成功)。

它山之石可以攻玉,相信會對你有所幫助。

為了能夠幫你提高研發能力(那怕是提高一丟丟呢),後續將繼續結合實際項目,看看用到的其它形式的配置文件,敬請期待。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 根據上上篇的鍵盤ui界面我添加了一個輸入框讓鍵盤有了輸入效果如下 界面代碼可以去上上篇看: https://www.cnblogs.com/2979100039-qq-con/p/12641603.html 這那個代碼基礎上加了一個輸入框,在把鍵盤縮放0.7倍就可以了 接下重點是js代碼同樣用的是j ...
  • 自己製作單選框樣式: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <titl ...
  • 申明:本文轉載至:https://github.com/dawn-plex/translate/blob/master/articles/5-Tips-to-Write-Better-Conditionals-in-JavaScript.md 感謝作者,感謝分享 原文地址:5 Tips to Wri ...
  • 最近看到一個有意思的圖片,包含了鮮為人知的秘密。。。 先看看這張有意思的圖片。 圖左應該講的是基督教中的三位一體。翻譯成中文如下。 當然這不是我們的重點,我們的重點在右邊這個圖。講的是js中相等操作。 是js中的寬鬆相等(loose equals)。 是嚴格相等(strict equals)。 這兩 ...
  • thrift類似java裡面的socket和sockchannel中server和client通信 thrift最重要的是跨語言,裡面提供了序列化和反序列化、json和實體對象等方法 Apache Thrift軟體框架(用於可擴展的跨語言服務開發)將軟體堆棧與代碼生成引擎結合在一起,以構建可在C++ ...
  • 作為一個碼農,你知道如何啟動一個java線程嗎? 啟動線程 public class PrintThread extends Thread { public void run() { System.out.println("我是線程! 繼承自Thread"); } public static voi ...
  • 全民閱讀的時代已經來臨,目前使用讀書軟體的用戶數2.1億,日活躍用戶超過500萬,其中19-35歲年輕用戶占比超過60%,本科及以上學歷用戶占比高達80%,北上廣深及其他省會城市/直轄市用戶占比超過80%。**本人習慣使用微信讀書,為了方便整理書籍和導出筆記,便開發了這個小工具。** ...
  • 前言 在【JAVA進階架構師指南】系列二和三中,我們瞭解了JVM的記憶體模型以及類載入機制,其中在記憶體模型中,我們說到,從線程角度來說,JVM分為線程私有的區域(虛擬機棧/本地方法棧/程式計數器)和線程公有區域(方法區和java堆),其中線程私有區域記憶體隨著線程的結束而跟著被回收,GC主要關註的是堆和 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...