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

Java Properties 配置文件讀取實戰:從入門到生產級最佳實踐

     編輯:Java 相關知識

前言:配置文件是應用與環境的契約

無論是數據庫連接參數、第三方服務地址、功能開關還是業務參數,將其外部化到 .properties 文件是 Java 應用的標準做法。本文從基本讀取開始,逐步深入到生產環境的類型安全訪問、多環境隔離、熱加載、加密解密等進階模式。

一、基礎讀取:三行代碼搞定


Properties props = new Properties();
props.load(new FileInputStream("src/jdbc.properties")); // 或 getResourceAsStream
String driver = props.getProperty("jdbc.driver");
String url = props.getProperty("jdbc.url");

關鍵點:

  • 路徑解析:FileInputStream 相對當前工作目錄;ClassLoader.getResourceAsStream() 相對 classpath 根目錄
  • 異常處理:load() 拋出 IOException,必須捕獲或拋出
  • 編碼:默認 ISO-8859-1,中文會亂碼(見下文解決方案)

二、中文編碼問題徹底解決

問題現象


# config.properties (UTF-8 編碼保存)
app.name=我的應用

props.load(new FileInputStream("config.properties"));
System.out.println(props.getProperty("app.name")); // 輸出:我的度用

原因:Properties.load(InputStream) 規範強制使用 ISO-8859-1 解碼。

方案 1:Java 11+ 原生支持 UTF-8


// 使用 Reader 顯式指定編碼
props.load(new InputStreamReader(new FileInputStream("config.properties"), StandardCharsets.UTF_8));
// 或 Files.newBufferedReader (Java 11+)
props.load(Files.newBufferedReader(Paths.get("config.properties"), StandardCharsets.UTF_8));

方案 2:轉義工具(兼容舊版本/JDK 規範)


// 寫入時:native2ascii 轉義或 IDE 自動轉義
// app.name=\u6211\u7684\u5E94\u7528
// 讀取時:自動還原為 Unicode 字符
props.load(new FileInputStream("config.properties")); // 正常讀取

建議:現代項目統一使用 UTF-8 + Reader 讀取,避免轉義帶來的可讀性損失和維護困難。

三、類型安全訪問:告別字符串解析


public class TypedProperties {
    private final Properties props;
    
    public TypedProperties(Properties props) { this.props = props; }
    
    public String getString(String key) { return props.getProperty(key); }
    public String getString(String key, String def) { return props.getProperty(key, def); }
    
    public int getInt(String key, int def) {
        String v = props.getProperty(key);
        return v != null ? Integer.parseInt(v) : def;
    }
    public long getLong(String key, long def) { /* 類似 */ }
    public boolean getBoolean(String key, boolean def) { /* 類似 */ }
    public Duration getDuration(String key, Duration def) { /* 解析 ISO-8601 或自定義格式 */ }
    public List getList(String key, String delimiter, List def) { /* 分割字符串 */ }
    
    // 泛型獲取(配合轉換器)
    public  T get(String key, Converter converter, T def) {
        String v = props.getProperty(key);
        return v != null ? converter.convert(v) : def;
    }
}

// 使用
TypedProperties tp = new TypedProperties(props);
int poolSize = tp.getInt("db.pool.size", 10);
Duration timeout = tp.getDuration("http.timeout", Duration.ofSeconds(30));
List servers = tp.getList("redis.nodes", ",", Collections.emptyList());

四、多環境配置隔離

目錄結構


src/main/resources/
├── application.properties      # 共享配置
├── application-dev.properties  # 開發環境
├── application-test.properties # 測試環境
└── application-prod.properties # 生產環境

加載邏輯


public Properties loadForProfile(String profile) {
    Properties props = new Properties();
    // 1. 基礎配置
    load(props, "application.properties");
    // 2. 環境特定配置(覆蓋基礎配置)
    load(props, "application-" + profile + ".properties");
    // 3. 本地覆蓋(不提交版本控制,用於開發機個性化)
    load(props, "application-local.properties");
    return props;
}

private void load(Properties props, String resource) {
    try (InputStream in = getClass().getClassLoader().getResourceAsStream(resource)) {
        if (in != null) props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
    } catch (IOException e) {
        log.warn("配置加載失敗: {}", resource, e);
    }
}

啟動參數指定環境:-Dspring.profiles.active=prod 或 -Dapp.profile=prod

五、配置熱加載:運行時感知變更


public class ReloadableConfig {
    private final Path configPath;
    private final WatchService watcher;
    private volatile TypedProperties current;
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    
    public ReloadableConfig(String configFile) throws IOException {
        this.configPath = Paths.get(configFile).toAbsolutePath();
        this.watcher = FileSystems.getDefault().newWatchService();
        configPath.getParent().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
        reload();
        scheduler.scheduleAtFixedRate(this::pollChanges, 1, 1, TimeUnit.SECONDS);
    }
    
    private void pollChanges() {
        try {
            WatchKey key;
            while ((key = watcher.poll()) != null) {
                for (WatchEvent event : key.pollEvents()) {
                    if (event.context().equals(configPath.getFileName())) {
                        reload();
                        break;
                    }
                }
                key.reset();
            }
        } catch (Exception e) {
            log.error("配置熱加載異常", e);
        }
    }
    
    private void reload() {
        try (Reader reader = Files.newBufferedReader(configPath, StandardCharsets.UTF_8)) {
            Properties props = new Properties();
            props.load(reader);
            this.current = new TypedProperties(props);
            log.info("配置已重新加載: {}", configPath);
        } catch (IOException e) {
            log.error("配置重新加載失敗", e);
        }
    }
    
    public TypedProperties get() { return current; }
    
    public void close() { watcher.close(); scheduler.shutdown(); }
}

適用場景:功能開關、業務閾值、限流參數等需頻繁調整無需重啟的配置。

六、敏感配置加密:Jasypt 實戰

1. 加密密文生成


// 命令行加密
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
    input="mysql_password" password="master_key" algorithm=PBEWithHMACSHA512AndAES_256
// 輸出:ENC(G6NvX8...)

2. 配置文件使用密文


# application-prod.properties
db.password=ENC(G6NvX8...)

3. Spring Boot 整合自動解密


// pom.xml

    com.github.ulisesbocchio
    jasypt-spring-boot-starter
    3.0.5


// application.yml
jasypt:
  encryptor:
    password: ${JASYPT_MASTER_PASSWORD} # 從環境變量讀取主密鑰
    algorithm: PBEWithHMACSHA512AndAES_256

// 代碼中直接獲取解密後的值
@Value("${db.password}")
String dbPassword; // 自動解密

七、總結:生產級配置管理清單

維度建議工具/技術
編碼UTF-8 + Reader 讀取StandardCharsets.UTF_8, Files.newBufferedReader
類型安全封裝 TypedProperties自定義轉換器、Duration/List 解析
多環境profile 分層覆蓋 + local 覆蓋application-{profile}.properties
熱加載WatchService + 定時輪詢java.nio.file.WatchService
敏感信息密文存儲 + 運行時解密Jasypt, HashiCorp Vault, AWS Secrets Manager
版本管理配置文件納入 Git,密文/密鑰不入庫.gitignore, .gitattributes
驗證啟動時校驗必填項、格式、範圍Bean Validation, 自定義校驗器

配置管理看似簡單,實則關係應用的可部署性、安全性、可運維性。從 Properties.load() 開始,構建屬於團隊的配置規範與工具鏈,讓配置成為加速交付的資產,而非故障的溫床。

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