現代應用面臨:本地 YAML/Properties、環境變量、命令行參數、配置中心、資料庫配置、遠程 HTTP 配置... 缺乏統一抽象會導致代碼散亂、難以測試、擴展困難。
public interface ConfigProvider {
/**
* 提供原始配置源對象
* @return 配置源 (Document, Properties, JsonNode, Yaml 等)
*/
T provide() throws ConfigException;
}
public interface ConfigLoader {
/**
* 加載並轉換為目標配置對象
*/
T load() throws ConfigException;
}
public abstract class AbstractConfigLoader implements ConfigLoader {
protected final ConfigProvider provider;
protected AbstractConfigLoader(ConfigProvider provider) {
this.provider = provider;
}
@Override
public final T load() throws ConfigException {
S source = provider.provide();
return doLoad(source);
}
protected abstract T doLoad(S source) throws ConfigException;
}
public class DomConfigProvider implements ConfigProvider {
private final String filePath;
public DomConfigProvider(String filePath) { this.filePath = filePath; }
@Override
public Document provide() throws ConfigException {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new File(filePath));
} catch (Exception e) {
throw new ConfigException("DOM 解析失敗: " + filePath, e);
}
}
}
public class PropertiesConfigProvider implements ConfigProvider {
private final String filePath;
public PropertiesConfigProvider(String filePath) { this.filePath = filePath; }
@Override
public Properties provide() throws ConfigException {
Properties props = new Properties();
try (InputStream is = new FileInputStream(filePath)) {
props.load(is);
return props;
} catch (IOException e) {
throw new ConfigException("Properties 加載失敗: " + filePath, e);
}
}
}
public class ConfigProviderFactory {
private ConfigProviderFactory() {}
public static ConfigProvider createDomProvider(String path) {
return new DomConfigProvider(path);
}
public static ConfigProvider createPropertiesProvider(String path) {
return new PropertiesConfigProvider(path);
}
// 擴展:YAML、JSON、Consul、Nacos、Apollo...
public static ConfigProvider createJsonProvider(String path) { ... }
public static ConfigProvider createYamlProvider(String path) { ... }
}
// MarketConfig 加載器 (XML 來源)
public class MarketConfigLoader extends AbstractConfigLoader {
public MarketConfigLoader(ConfigProvider provider) {
super(provider);
}
@Override
protected MarketConfig doLoad(Document doc) throws ConfigException {
MarketConfig config = new MarketConfig();
// 解析 AppleConfig
Element appleEl = (Element) doc.getElementsByTagName("apple").item(0);
AppleConfig apple = new AppleConfig();
apple.setColor(getTextContent(appleEl, "color"));
apple.setPrice(Integer.parseInt(getTextContent(appleEl, "price")));
config.setAppleConfig(apple);
// 解析 EggConfig
Element eggEl = (Element) doc.getElementsByTagName("egg").item(0);
EggConfig egg = new EggConfig();
egg.setWeight(Integer.parseInt(getTextContent(eggEl, "weight")));
config.setEggConfig(egg);
// 委派加載 OwnerConfig (不同來源)
ConfigLoader ownerLoader = new OwnerConfigLoader(
ConfigProviderFactory.createPropertiesProvider("owner.properties")
);
config.setOwnerConfig(ownerLoader.load());
return config;
}
private String getTextContent(Element parent, String tagName) {
NodeList list = parent.getElementsByTagName(tagName);
return list.getLength() > 0 ? list.item(0).getTextContent() : "";
}
}
// OwnerConfig 加載器 (Properties 來源)
public class OwnerConfigLoader extends AbstractConfigLoader {
public OwnerConfigLoader(ConfigProvider provider) {
super(provider);
}
@Override
protected OwnerConfig doLoad(Properties props) throws ConfigException {
OwnerConfig config = new OwnerConfig();
props.forEach((k, v) -> config.addOwner((String) k, (String) v));
return config;
}
}
public class ConfigBootstrap {
public static void main(String[] args) throws ConfigException {
// 構建加載鏈
ConfigLoader loader = new MarketConfigLoader(
ConfigProviderFactory.createDomProvider("market.xml")
);
// 一鍵加載
MarketConfig config = loader.load();
System.out.println("蘋果顏色: " + config.getAppleConfig().getColor());
System.out.println("檔口老闆: " + config.getOwnerConfig().getOwners());
}
}
public class MergingConfigLoader implements ConfigLoader {
private final List> loaders; // 按優先級遞減
private final Merger merger;
@Override
public T load() throws ConfigException {
T result = null;
for (ConfigLoader loader : loaders) {
T current = loader.load();
result = (result == null) ? current : merger.merge(result, current);
}
return result;
}
}
public interface ConfigChangeListener {
void onChange(String configKey, Object oldValue, Object newValue);
}
public class WatchingConfigLoader implements ConfigLoader {
private final ConfigLoader delegate;
private final FileWatcher watcher;
private final List listeners = new CopyOnWriteArrayList<>();
public T load() throws ConfigException {
return delegate.load();
}
public void addListener(ConfigChangeListener listener) {
listeners.add(listener);
}
// 內部定時掃描或 inotify 觸發重新加載並通知
}
這套機制核心價值:
這正是 Spring Environment/PropertySource、Apache Commons Configuration、MicroProfile Config 等框架的核心設計思想。