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

Java 配置加載機制設計:多源融合、擴展性與實戰模式

     編輯:Java 相關知識

痛點:配置地獄

現代應用面臨:本地 YAML/Properties、環境變量、命令行參數、配置中心、資料庫配置、遠程 HTTP 配置... 缺乏統一抽象會導致代碼散亂、難以測試、擴展困難。

核心抽象設計

1. 配置提供者介面

public interface ConfigProvider {
    /**
     * 提供原始配置源對象
     * @return 配置源 (Document, Properties, JsonNode, Yaml 等)
     */
    T provide() throws ConfigException;
}

2. 配置加載器介面

public interface ConfigLoader {
    /**
     * 加載並轉換為目標配置對象
     */
    T load() throws ConfigException;
}

3. 抽象基類:模板方法模式

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;
}

具體實現:多源融合案例

場景

  • market.xml → MarketConfig (含 AppleConfig、EggConfig)
  • owner.properties → OwnerConfig
  • 合併為完整 MarketConfig

DOM 配置提供者

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);
        }
    }
}

Properties 配置提供者

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 觸發重新加載並通知
}

總結

這套機制核心價值:

  1. 關注點分離:提供者管「從哪讀」,加載器管「怎麼轉」
  2. 開閉原則:新增配置源只需實現 Provider,不修改既有代碼
  3. 可測試性:輕鬆 Mock Provider 進行單元測試
  4. 統一入口:業務代碼只依賴 ConfigLoader 介面

這正是 Spring Environment/PropertySource、Apache Commons Configuration、MicroProfile Config 等框架的核心設計思想。

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