首頁  >  文章  >  Java  >  讀取properties檔案的6種方式,建議收藏!

讀取properties檔案的6種方式,建議收藏!

Java后端技术全栈
Java后端技术全栈轉載
2023-08-15 16:03:24525瀏覽

手寫分散式設定中心一步一腳印正在進行中。

這年頭基本上都是使用Spring Boot開發,然後都知道在專案中會有一個application.properties設定檔(也有的是application.yaml,反正就是用來保存我們的一些設定資訊),通常我們會把一些設定資訊寫到properties檔案中,例如:資料庫連線資訊、第三方介面資訊(金鑰、使用者名稱、密碼、位址等) ,連接池、Redis配置資訊、各種第三方組件配置資訊等。

單體服務,甚至在一些小型的分散式架構中,專案的配置都是依賴一個application.properties設定檔來解決(可能有的專案會搞一個環境區分,例如:application-dev.propertiesapplication-pro.properties等)。不過,可能伴隨著業務的發展和架構不斷升級,服務的資料以及每個服務涉及到配置資訊會越來越多,並且對於配置管理的要求也是越來越高,例如配置資訊的即時性、獨立性等。

同時,我們在微服務架構下,可能還會涉及到不同環境下的組態管理、灰階發布、動態限流、動態降級等需求,包括對於設定內容的安全與權限,所以傳統的配置維護方式很難達到需求。

於是,分散式配置中心就在這樣的環境下產生了。

本文我們先搞清楚java中讀取properties設定文件,到底有哪些方法。

Java讀取properties設定檔的6種方式

需求背景

需求是我們專案中有個jdbc.properties 設定文件,內如下:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456

現在是想要在java程式碼中取得上面設定檔內容。

第一種方式

第一種方式我們採用:this.getClass().getResourceAsStream() Properties

程式碼實作:

/**
* @author tianwc  公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年05月27日 09:13
* 在线刷题1200+,100+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
*/
public void readProperties1() throws IOException {
    //不加/会从当前包进行寻找,加上/会从src开始找
    InputStream inputStream = this.getClass().getResourceAsStream("/jdbc.properties");
    Properties properties=new Properties();
    properties.load(inputStream);
    System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver"));
    System.out.println("jdbc.url="+properties.getProperty("jdbc.url"));
    System.out.println("jdbc.username="+properties.getProperty("jdbc.username"));
    System.out.println("jdbc.password="+properties.getProperty("jdbc.password"));
}

下面來聊聊上面的這段程式碼:

##this. getClass().getResourceAsStream()

特定檔案和程式碼的位置是,程式碼在

src/main/java目錄下,資源檔案在src/main/resources/目錄下。

會從目前類別的目錄下去找,這個檔案如果不跟該類別在一個目錄下,就找不到。

會從編譯後的整個classes目錄下去找,maven也會把資源檔打包進classes資料夾,所以可以找到。

ClassLoader就是從整個classes資料夾找的,所以前面不用再加/

Properties

Properties:java.util.Properties,該類別主要用於讀取Java的配置文件,不同的程式語言有自己所支援的配置文件,設定檔中很多變數是經常改變的,為了方便使用者的配置,能讓使用者夠脫離程式本身去修改相關的變數設定。就像在Java中,其設定檔常為.properties文件,是以鍵值對的形式進行參數配置的。

類別關係圖:

讀取properties檔案的6種方式,建議收藏!

從上面的類別圖可以看到Properties類別繼承至Hashtable,相信大家都知道Hashtable是儲存key-value資料結構類,也剛好對應我們properties檔案內容也是key-value形式。

Properties 常見方法

#getProperty(String key)   :在此屬性清單中搜尋具有指定鍵的屬性。如果在此屬性清單中找不到該鍵,則會檢查預設屬性清單及其預設值(遞歸)。如果未找到該屬性,則該方法傳回預設值參數。

list(PrintStream out) 將此屬性清單列印到指定的輸出流。此方法對於調試很有用。

load(InputStream inStream)  :從輸入位元組流讀取屬性清單(鍵和元素對)。輸入流採用載入(Reader)中指定的簡單的面向行的格式,並假定使用ISO 8859-1字元編碼;即每個位元組是一個Latin1字元。不在Latin1中的字元和某些特殊字元在使用Unicode轉義符的鍵和元素中表示。此方法返回後,指定的流仍保持開啟狀態。

setProperty(String key, String  value) :呼叫 Hashtable 的方法 put 。他透過呼叫基底類別的put方法來設定 鍵值對。

store(OutputStream out, String comments) :將此Properties表中的此屬性清單(鍵和元素對)以適合使用load(InputStream)方法載入到Properties表的格式寫入輸出流。此Properties方法不會寫出此Properties表的defaults表中的屬性(如果有)。

storeToXML(OutputStream os, String comment, String encoding) :使用指定的編碼發出表示此表中包含的所有屬性的XML文件。

clear()  :清除此雜湊表,使其不包含任何鍵。

stringPropertyNames()  :傳回此屬性清單中的一組鍵,其中鍵及其對應的值是字串,如果尚未從主屬性清單中找到相同名稱的鍵,則包括預設屬性清單中的不同鍵。鍵或鍵不是String類型的屬性將被省略。

properties.load(inputStream)
public synchronized void load(InputStream inStream) throws IOException {
        load0(new LineReader(inStream));
    }

    private void load0 (LineReader lr) throws IOException {
        char[] convtBuf = new char[1024];
        int limit;
        int keyLen;
        int valueStart;
        char c;
        boolean hasSep;
        boolean precedingBackslash;
        //逐行读取
        while ((limit = lr.readLine()) >= 0) {
            c = 0;
            keyLen = 0;
            valueStart = limit;
            hasSep = false;

            //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
            precedingBackslash = false;
            while (keyLen < limit) {
                c = lr.lineBuf[keyLen];
                //need check if escaped.
                if ((c == &#39;=&#39; ||  c == &#39;:&#39;) && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    hasSep = true;
                    break;
                } else if ((c == &#39; &#39; || c == &#39;\t&#39; ||  c == &#39;\f&#39;) && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    break;
                }
                if (c == &#39;\\&#39;) {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }
            while (valueStart < limit) {
                c = lr.lineBuf[valueStart];
                if (c != &#39; &#39; && c != &#39;\t&#39; &&  c != &#39;\f&#39;) {
                    if (!hasSep && (c == &#39;=&#39; ||  c == &#39;:&#39;)) {
                        hasSep = true;
                    } else {
                        break;
                    }
                }
                valueStart++;
            }
            //前面一堆代码就是做校验和解析
            //下面两个是做转换
            String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
            String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
            
            put(key, value);
        }
    }

最后调用put(key, value);这个put方法就是Hashtable中的put方法。这里可以这么理解:将我们的配置项保存到Hashtable中。

getProperty(String key)
public String getProperty(String key) {
    Object oval = super.get(key);
    String sval = (oval instanceof String) ? (String)oval : null;
    return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}

super.get(key);就是调用Hashtable中的get()方法,也就是此时返回value,同时这就对应返回了properties文件中key对应的value。

第二种方式

第二种方式,我们通过当前类的加载器进行读取this.getClass().getClassLoader().getResourceAsStream()获取InputStream。

代码实现:

/**
* @author tianwc  公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年05月27日 09:13
* 博客地址:<a href="http://woaijava.cc/">在线刷题1200+,100+篇干货文章</a>
*/
public void readProperties2() throws IOException {
    //不加/,若加了会为null
    InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
    //如果放在config目录下
    //InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config/jdbc.properties");
    Properties properties=new Properties();
    properties.load(inputStream);
    System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver"));
    System.out.println("jdbc.url="+properties.getProperty("jdbc.url"));
    System.out.println("jdbc.username="+properties.getProperty("jdbc.username"));
    System.out.println("jdbc.password="+properties.getProperty("jdbc.password"));
}

第一看怎么觉得和第一种方式很像,下面来说说两个的区别。

  • this.getClass.getResourceAsStream() 從目前類別所在的位置開始尋找設定檔位置。要找到jdbc.properties必須加/classpath下開始尋找
  • ##this.getClass( ).getClassLoader().getResourceAsStream() 預設就從classpath路徑下開始查找,加上/會報空指標異常
剩下的部分程式碼和第一種方式一樣,這裡就不在贅述了。

第三種方式

#接下來我們採用

ClassLoader類別的static方法getSystemResourceAsStream()

/**
* @author tianwc  公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年05月27日 09:13
* 博客地址:<a href="http://woaijava.cc/">在线刷题1200+,100+篇干货文章</a>
*/
public void readProperties3() throws IOException {
    //如果存放到config目录下
    //InputStream inputStream = ClassLoader.getSystemResourceAsStream("config/jdbc.properties");
    InputStream inputStream = ClassLoader.getSystemResourceAsStream("jdbc.properties");
    Properties properties=new Properties();
    properties.load(inputStream);
    System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver"));
    System.out.println("jdbc.url="+properties.getProperty("jdbc.url"));
    System.out.println("jdbc.username="+properties.getProperty("jdbc.username"));
    System.out.println("jdbc.password="+properties.getProperty("jdbc.password"));
}

ClassLoader中的getSystemResourceAsStream()方法,它用于获取资源作为参数并将资源转换为InputStream。例如,我们可以使用该方法获取网站的静态资源并将其转换为InputStream

说白了就是获取InputStream的方式不同罢了,最终还是交给Properties去解析jdbc.properties文件内容。

第四种方式

我们在实际开发中,基本上都是离不开Spring了,所以,接下来我们使用Spring中的 ClassPathResource读取配置文件。

代码实现:

/**
* @author tianwc  公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年05月27日 09:13
* 博客地址:<a href="http://woaijava.cc/">博客地址</a>
*/
public void readProperties4() throws IOException {
    ClassPathResource resource = new ClassPathResource("jdbc.properties");
    //ClassPathResource resource = new ClassPathResource("config/jdbc.properties");
    Properties properties= PropertiesLoaderUtils.loadProperties(resource);
    System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver"));
    System.out.println("jdbc.url="+properties.getProperty("jdbc.url"));
    System.out.println("jdbc.username="+properties.getProperty("jdbc.username"));
    System.out.println("jdbc.password="+properties.getProperty("jdbc.password"));
}

这里PropertiesLoaderUtils是spring-core.jar下面的,全路径名称:

org.springframework.core.io.support.PropertiesLoaderUtils

PropertiesLoaderUtils.loadProperties(resource)源码部分:

public static Properties loadProperties(EncodedResource resource) throws IOException {
    //创建一个Properties对象
    Properties props = new Properties();
    //处理文件内容并赋值给props
    fillProperties(props, resource);
    return props;
}

fillProperties(props, resource);方法:

public static void fillProperties(Properties props, EncodedResource resource) throws IOException {
    fillProperties(props, resource, ResourcePropertiesPersister.INSTANCE);
}

static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) throws IOException {
    InputStream stream = null;
    Reader reader = null;

    try {
        //省略不相关代码
        stream = resource.getInputStream();
        //获取InputStream
        persister.load(props, stream); 
    } finally {
        //关闭
    }
}

最后到PropertiesPersisterpersister.load(props, stream);

public void load(Properties props, InputStream is) throws IOException {
    props.load(is);
}

这里又回到Properties类中的load()方法里了。

绕了半天也只是获取InputStream的方式不同而已

第五种方式

接下来我们来使用PropertyResourceBundle读取InputStream流,实现配置文件读取。

代码实现:

public void readProperties5() throws IOException {
    InputStream inputStream = ClassLoader.getSystemResourceAsStream("jdbc.properties");
    //InputStream inputStream = ClassLoader.getSystemResourceAsStream("config/jdbc.properties");
    PropertyResourceBundle bundle = new PropertyResourceBundle(inputStream);
    System.out.println(bundle.getString("jdbc.driver"));
    System.out.println(bundle.getString("jdbc.url"));
    System.out.println(bundle.getString("jdbc.username"));
    System.out.println(bundle.getString("jdbc.password"));
}

好像也没什么,

PropertyResourceBundle源码

我们来看看 new PropertyResourceBundle(inputStream);源码部分:

public PropertyResourceBundle (InputStream stream) throws IOException {
    Properties properties = new Properties();
    properties.load(stream);
    lookup = new HashMap(properties);
}

这个构造方法里直接new了一个Properties对象。然后调用load方法解析。

所以,这种方式无非就是在Properties基础之上再封装了,也就是让我们使用起来更加方便。

PropertyResourceBundle类关系图

讀取properties檔案的6種方式,建議收藏!

所以,上面代码中的bundle.getString("jdbc.url")其实调用的是父类中方法;

public final String getString(String key) {
    return (String) getObject(key);
}

最终调用到PropertyResourceBundlehandleGetObject()方法:

public Object handleGetObject(String key) {
    if (key == null) {
        throw new NullPointerException();
    }
    return lookup.get(key);
}

lookup就是一个HashMap:lookup = new HashMap(properties);

第六种方式

第五种方式中我们看到了ResourceBundle,接下来我们就是用ResourceBundle.getBundle()实现。

//不用输入后缀
public void readProperties6()  {
    ResourceBundle bundle=ResourceBundle.getBundle("jdbc"); 
    System.out.println(bundle.getString("jdbc.driver"));
    System.out.println(bundle.getString("jdbc.url"));
    System.out.println(bundle.getString("jdbc.username"));
    System.out.println(bundle.getString("jdbc.password"));
}

直接使用文件名称就可以了,不需要写文件后缀名。

java.util.ResourceBundle.getBundle(String baseName) 方法获取使用指定的基本名称,不需要文件后缀名,默认的语言环境和调用者的类加载器获取资源包。

  • 如果 baseName 為 null ,則封包異常NullPointerException
  • 如果可以找到指定的基底沒有相應的資源包,則封包異常MissingResourceException

##總結

############################### ####以上就是我們通常在java中讀取properties檔案的6中方式。 #####################

以上是讀取properties檔案的6種方式,建議收藏!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:Java后端技术全栈。如有侵權,請聯絡admin@php.cn刪除