項目中常需讀取 db.properties、config.properties 等配置文件,獲取數據庫連接參數、業務開關等。關鍵難點:**路徑定位穩健**、**配置緩存熱加載**、**線程安全**。
FileInputStream fis = new FileInputStream("D:/project/config/db.properties"); // 絕對路徑
// 或
FileInputStream fis = new FileInputStream("src/db.properties"); // 相對路徑,部署後失效
痛點:遷移環境需改代碼,打包後路徑失效,維護成本極高。
public class ConfigLoader {
private Properties props = new Properties();
public ConfigLoader() {
try (InputStream is = getClass().getResourceAsStream("/db.properties")) {
props.load(is);
} catch (IOException e) { throw new RuntimeException(e); }
}
public String get(String key) { return props.getProperty(key); }
}
優點:無需關心物理路徑,打包進 JAR 同級目錄即可訪問。
缺點:構造器中初始化,無法實現靜態緩存;每次 new 都會重複讀取 IO。
public final class DbConfig {
private static String URL;
private static String USER;
private static String PASS;
static {
load();
}
private static synchronized void load() {
if (URL != null) return; // 雙重檢查
try (InputStream is = DbConfig.class.getResourceAsStream("/db.properties")) {
Properties props = new Properties();
props.load(is);
URL = props.getProperty("jdbc.url");
USER = props.getProperty("jdbc.user");
PASS = props.getProperty("jdbc.password");
} catch (IOException e) {
throw new ExceptionInInitializerError("配置文件加載失敗: " + e.getMessage());
}
}
public static String getUrl() { return URL; }
public static String getUser() { return USER; }
public static String getPass() { return PASS; }
}
亮點:
synchronized 保證多線程環境下單次加載;ExceptionInInitializerError 快速失敗,暴露配置缺失。public class ReloadableConfig {
private static final AtomicReference<Properties> REF = new AtomicReference<>();
private static final ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor();
static {
reload();
SCHEDULER.scheduleAtFixedRate(ReloadableConfig::reload, 1, 1, TimeUnit.MINUTES);
}
private static void reload() {
try (InputStream is = ReloadableConfig.class.getResourceAsStream("/config.properties")) {
Properties props = new Properties();
props.load(is);
REF.set(props);
} catch (IOException e) {
log.error("配置熱加載失敗", e);
}
}
public static String get(String key) {
return REF.get().getProperty(key);
}
}
getResourceAsStream("/xxx.properties"):前導斜杠表示 classpath 根目錄(即 src/main/resources),無斜杠則相對當前類包路徑。resources,或統一放在獨立 config-module 依賴引入。「靜態塊 + ClassLoader + 雙重檢查鎖」是讀取 classpath 配置的工業級標準寫法,兼顧性能、線程安全、部署無關性。若需動態刷新,引入定時任務 + AtomicReference 即可平滑擴展。