首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

J2SE API 讀取 Properties 設定檔的六種標準方式完整指南

     編輯:Java 相關知識

Java 標準庫提供多種途徑載入 .properties 設定檔,適用於不同部署場景(檔案系統、Classpath、JAR 內部、Servlet 容器、模組化應用)。本文系統整理 J2SE 六種核心 API 用法,附上完整可執行範例程式碼與單元測試,並補充 Servlet 環境下的第七種方式,幫助開發者根據場景選擇最適當的載入策略。

一、六種核心載入方式對比表

編號API 類別/方法資料來源適用場景關鍵特性
1Properties.load(InputStream)檔案系統路徑獨立應用、絕對/相對路徑最通用、支援任意 InputStream
2ResourceBundle.getBundle(baseName)Classpath (資源束)國際化訊息、Classpath 資源自動依 Locale 解析變體、快取機制
3new PropertyResourceBundle(InputStream)檔案系統/任意流需 ResourceBundle 介面的舊代碼整合橋接 Properties 與 ResourceBundle
4Class.getResourceAsStream(path)Classpath (相對類別位置)同包資源、模組內部資源相對路徑解析、模組化友善
5ClassLoader.getResourceAsStream(path)Classpath (根路徑)跨包資源、類別載入器隔離場景絕對路徑 (從 classpath 根)、支援多載入器
6ClassLoader.getSystemResourceAsStream(path)系統類別載入器 Classpath啟動類別載入器可見資源、舊版相容靜態方法、無需 Class 實例

二、完整工具類實作:JProperties.java

package com.kindani;

import java.util.*;
import java.io.*;

public class JProperties {
    public static final int BY_PROPERTIES = 1;
    public static final int BY_RESOURCEBUNDLE = 2;
    public static final int BY_PROPERTYRESOURCEBUNDLE = 3;
    public static final int BY_CLASS = 4;
    public static final int BY_CLASSLOADER = 5;
    public static final int BY_SYSTEM_CLASSLOADER = 6;

    public static Properties loadProperties(final String name, final int type) throws IOException {
        Properties props = new Properties();
        InputStream in = null;
        
        try {
            switch (type) {
                case BY_PROPERTIES:
                    in = new BufferedInputStream(new FileInputStream(name));
                    props.load(in);
                    break;
                    
                case BY_RESOURCEBUNDLE:
                    ResourceBundle rb = ResourceBundle.getBundle(name, Locale.getDefault());
                    props = new ResourceBundleAdapter(rb);
                    break;
                    
                case BY_PROPERTYRESOURCEBUNDLE:
                    in = new BufferedInputStream(new FileInputStream(name));
                    rb = new PropertyResourceBundle(in);
                    props = new ResourceBundleAdapter(rb);
                    break;
                    
                case BY_CLASS:
                    in = JProperties.class.getResourceAsStream(name);
                    if (in == null) throw new FileNotFoundException("Classpath resource not found: " + name);
                    props.load(in);
                    break;
                    
                case BY_CLASSLOADER:
                    in = JProperties.class.getClassLoader().getResourceAsStream(name);
                    if (in == null) throw new FileNotFoundException("ClassLoader resource not found: " + name);
                    props.load(in);
                    break;
                    
                case BY_SYSTEM_CLASSLOADER:
                    in = ClassLoader.getSystemResourceAsStream(name);
                    if (in == null) throw new FileNotFoundException("System ClassLoader resource not found: " + name);
                    props.load(in);
                    break;
                    
                default:
                    throw new IllegalArgumentException("Unknown type: " + type);
            }
        } finally {
            if (in != null) try { in.close(); } catch (IOException ignored) {}
        }
        return props;
    }

    // Servlet 環境專用 (第 7 種方式)
    public static Properties loadProperties(javax.servlet.ServletContext context, String path) throws IOException {
        try (InputStream in = context.getResourceAsStream(path)) {
            if (in == null) throw new FileNotFoundException("ServletContext resource not found: " + path);
            Properties props = new Properties();
            props.load(in);
            return props;
        }
    }

    // ResourceBundle 轉 Properties 適配器
    public static class ResourceBundleAdapter extends Properties {
        private final ResourceBundle bundle;
        
        public ResourceBundleAdapter(ResourceBundle rb) {
            this.bundle = rb;
            Enumeration<String> keys = rb.getKeys();
            while (keys.hasMoreElements()) {
                String key = keys.nextElement();
                put(key, rb.getObject(key));
            }
        }
        // 代理常用方法...
        public ResourceBundle getBundle(String baseName) { return ResourceBundle.getBundle(baseName); }
        public ResourceBundle getBundle(String baseName, Locale locale) { return ResourceBundle.getBundle(baseName, locale); }
        public Enumeration<String> getKeys() { return bundle.getKeys(); }
        public Locale getLocale() { return bundle.getLocale(); }
        public Object getObject(String key) { return bundle.getObject(key); }
        public String getString(String key) { return bundle.getString(key); }
        public String[] getStringArray(String key) { return bundle.getStringArray(key); }
    }
}

三、關鍵路徑規則與常見坑

方式路徑格式範例注意事項
FileInputStream檔案系統絕對/相對路徑C:/config/app.properties
config/app.properties
相對路徑基於 JVM 工作目錄 (user.dir),部署易出錯
Class.getResourceAsStream相對路徑 (不以 / 開頭)com/kindani/test/LocalStrings.properties相對於該類別的包目錄;以 / 開頭則從 classpath 根
ClassLoader.getResourceAsStream絕對路徑 (從 classpath 根,不以 / 開頭)com/kindani/test/LocalStrings.properties統一從 classpath 根解析,跨包引用首選
ResourceBundle.getBundle基礎名稱 (套件.檔名,無副檔名)com.kindani.test.LocalStrings自動附加 _zh_TW 等 Locale 變體,屬性檔需放在 classpath

四、單元測試驗證:JPropertiesTest.java

package com.kindani.test;

import junit.framework.*;
import com.kindani.JProperties;
import java.util.Properties;

public class JPropertiesTest extends TestCase {
    private static final String KEY = "helloworld.title";
    private static final String VALUE = "Hello World!";

    public void testLoadProperties() throws Exception {
        Properties props;
        
        // 1. 檔案系統路徑
        props = JProperties.loadProperties("C:/IDEAP/Properties4Methods/src/com/kindani/test/LocalStrings.properties", 
                                           JProperties.BY_PROPERTIES);
        assertEquals(VALUE, props.getProperty(KEY));
        
        // 2. ResourceBundle (classpath)
        props = JProperties.loadProperties("com.kindani.test.LocalStrings", JProperties.BY_RESOURCEBUNDLE);
        assertEquals(VALUE, props.getProperty(KEY));
        
        // 3. PropertyResourceBundle
        props = JProperties.loadProperties("C:/IDEAP/Properties4Methods/src/com/kindani/test/LocalStrings.properties",
                                           JProperties.BY_PROPERTYRESOURCEBUNDLE);
        assertEquals(VALUE, props.getProperty(KEY));
        
        // 4. 系統類別載入器
        props = JProperties.loadProperties("com/kindani/test/LocalStrings.properties", JProperties.BY_SYSTEM_CLASSLOADER);
        assertEquals(VALUE, props.getProperty(KEY));
        
        // 5. 類別載入器
        props = JProperties.loadProperties("com/kindani/test/LocalStrings.properties", JProperties.BY_CLASSLOADER);
        assertEquals(VALUE, props.getProperty(KEY));
        
        // 6. Class 相對路徑
        props = JProperties.loadProperties("testLocalStrings.properties", JProperties.BY_CLASS);
        assertEquals(VALUE, props.getProperty(KEY));
    }
}

五、測試用 Properties 檔案:LocalStrings.properties

# $Id: LocalStrings.properties,v 1.1 2000/08/17 00:57:52 horwat Exp $
# Default localized resources for example servlets
# This locale is en_US
helloworld.title=Hello World!
requestinfo.title=Request Information Example
requestinfo.label.method=Method:
requestinfo.label.requesturi=Request URI:
requestinfo.label.protocol=Protocol:
requestinfo.label.pathinfo=Path Info:
requestinfo.label.remoteaddr=Remote Address:
requestheader.title=Request Header Example
requestparams.title=Request Parameters Example
requestparams.params-in-req=Parameters in this request:
requestparams.no-params=No Parameters, Please enter some
requestparams.firstname=First Name:
requestparams.lastname=Last Name:
cookies.title=Cookies Example
cookies.cookies=Your browser is sending the following cookies:
cookies.no-cookies=Your browser isn't sending any cookies
cookies.make-cookie=Create a cookie to send to your browser
cookies.name=Name:
cookies.value=Value:
cookies.set=You just sent the following cookie to your browser:
sessions.title=Sessions Example
sessions.id=Session ID:
sessions.created=Created:
sessions.lastaccessed=Last Accessed:
sessions.data=The following data is in your session:
sessions.adddata=Add data to your session
sessions.dataname=Name of Session Attribute:
sessions.datavalue=Value of Session Attribute:

六、選型決策指南

  1. 獨立應用、外部配置檔:Properties.load(FileInputStream) 或 Files.readString(Path) (Java 11+)
  2. Classpath 內部資源、需國際化:ResourceBundle.getBundle() (自動 Locale 解析、快取)
  3. Classpath 內部資源、無國際化需求:ClassLoader.getResourceAsStream() + Properties.load() (最通用、可控)
  4. 模組化應用 (JPMS):Module.getResourceAsStream() 或 Class.getResourceAsStream() (模組邊界隔離)
  5. Servlet / Web 容器:ServletContext.getResourceAsStream() (支援 WAR 內部、外部化配置)
  6. Spring / 框架整合:使用框架提供的 @PropertySource、Environment、ConfigurablePropertyResolver 抽象層

七、效能與記憶體考量

  • ResourceBundle 內建快取,重複 getBundle 極快,但修改檔案需重啟或手動清除快取 (ResourceBundle.clearCache() Java 8+)
  • Properties 無快取,每次載入皆讀取流,適合動態重載場景
  • 大型配置檔建議啟動時一次性載入,避免熱路徑重複 I/O

八、結語

掌握六種標準 API 的適用邊界與路徑解析規則,是 Java 開發者處理配置管理的基本功。實務中建議封裝為統一的 ConfigurationService,屏蔽底層載入細節,提供型別安全的 getInt()、getDuration() 等存取方法,並整合框架配置抽象層,實現從開發到生產的無縫配置管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved