無論專案大小,資料庫連線、環境參數、第三方服務地址等資訊絕不能硬編碼在程式中。硬編碼弊端:
主流配置格式:.properties(鍵值對)、.xml(複雜結構)、.yaml/.yml(現代化、層次清晰)。
Spring 提供統一資源載入抽象 `ResourceLoader`,預設實現 `DefaultResourceLoader` 支援多種路徑前綴:
public Resource getResource(String location) {
// 1. 協議解析器優先嘗試解析
for (ProtocolResolver resolver : protocolResolvers) {
Resource r = resolver.resolve(location, this);
if (r != null) return r;
}
// 2. 內建協議處理
if (location.startsWith("/")) return getResourceByPath(location);
if (location.startsWith("classpath:")) return new ClassPathResource(...);
try {
return new UrlResource(new URL(location));
} catch (MalformedURLException e) {
return getResourceByPath(location); // 退回檔案路徑
}
}
方案二:Apache Commons Configuration
專門處理配置的輕量庫(~400KB),設計精良,支援多種格式與來源合併。
兩行代碼搞定
PropertiesConfiguration config = new PropertiesConfiguration("log4j.properties"); String value = config.getString("log4j.appender.file");核心載入邏輯
public void load(String fileName) throws ConfigurationException { URL url = ConfigurationUtils.locate(fileSystem, basePath, fileName); if (url == null) throw new ConfigurationException("Cannot locate: " + fileName); load(url); }進階特性
若專案極簡、不想引入第三方 JAR,或追求極致輕量,JDK 原生 API 完全足夠。
// 方式 1:類載入器獲取 URL(推薦,支援 classpath)
URL resource = Thread.currentThread().getContextClassLoader().getResource("config.properties");
Properties props = new Properties();
props.load(resource.openStream());
String value = props.getProperty("key");
String url = MyClass.class.getResource("").getPath().replaceAll("%20", " ");
String path = url.substring(0, url.indexOf("classes")) + "config.properties";
try (InputStream is = new FileInputStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
Properties props = new Properties();
props.load(br);
String value = props.getProperty("key");
}
| 維度 | Spring ResourceLoader | Apache Commons Configuration | 純 JDK |
|---|---|---|---|
| 依賴大小 | Spring Core (~1.5MB) | ~400KB | 0 KB |
| 路徑解析能力 | 極強(多協議) | 中等 | 基礎 |
| 型別轉換 | 需手動 | 內建豐富 | 需手動 |
| 熱重載 | 需額外配置 | 內建支援 | 需自行實現 |
| 適用場景 | Spring 專案 | 非 Spring/獨立模組 | 極簡專案/工具類 |
public final class ConfigUtil {
private static final Properties PROPS = new Properties();
private static final String DEFAULT_CONFIG = "application.properties";
private static volatile boolean initialized = false;
static {
load(DEFAULT_CONFIG);
}
public static synchronized void load(String configPath) {
if (initialized) return;
try (InputStream is = Thread.currentThread()
.getContextClassLoader().getResourceAsStream(configPath)) {
if (is == null) {
throw new IllegalStateException("Config not found: " + configPath);
}
PROPS.load(new InputStreamReader(is, StandardCharsets.UTF_8));
initialized = true;
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
public static String get(String key) {
return PROPS.getProperty(key);
}
public static String get(String key, String defaultValue) {
return PROPS.getProperty(key, defaultValue);
}
public static int getInt(String key, int defaultValue) {
String v = PROPS.getProperty(key);
return v != null ? Integer.parseInt(v) : defaultValue;
}
public static boolean getBoolean(String key, boolean defaultValue) {
String v = PROPS.getProperty(key);
return v != null ? Boolean.parseBoolean(v) : defaultValue;
}
}
配置管理看似簡單,實則關係專案可維護性與部署靈活性。選對工具,事半功倍。