关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java实现配置加载机制

    Java实现配置加载机制

    2016-01-22 00:00:00 出处:__kingzone__的专栏
    分享

    前言

    现如今几乎大多数Java应用,例如我们耳熟能详的tomcat, struts2, netty…等等数都数不过来的软件,要满足通用性,都会提供配置文件供使用者定制功能。

    甚至有一些例如Netty这样的网络框架,几乎完全就是由配置驱动,这样的软件我们也通常称之为”微内核架构”的软件。你把它配置成什么,它就是什么。

    It is what you configure it to be.

    最常见的配置文件格式是XML, Properties等等文件。

    本文探讨加载配置中最通用也是最常见的场景,那就是把一个配置文件映射成Java里的POJO对象.

    并探讨如何实现不同方式的加载,例如,有一些配置是从本地XML文件里面加载的,而有一些配置需要从本地Properties文件加载,

    更有甚者,有一些配置需要通过网络加载配置。

    如何实现这样一个配置加载机制,让我们拥有这个机制后,不会让加载配置的代码散布得到处都是,并且可扩展,可管理。

    配置加载器

    首先,我们需要一个配置加载器,而这个配置加载器是可以有多种不同的加载方式的,因此,我们用一个接口来描述它,如下所示:

    /**
     * 
     *
     * @author Bean
     * @date 2016年1月21日 上午11:47:12
     * @version 1.0
     *
     */
    public interface IConfigLoader<T> {
    
        /**
         * load the config typed by T
         *
         * @return
         * @throws ConfigException
         */
        public T load() throws ConfigException;
    }

    可是,为什么我们需要在这个接口上声明泛型 <T> ?

    很明显,当我们要使用一个配置加载器时,你得告诉这个配置加载器你需要加载后得到什么结果。

    例如,你希望加载配置后得到一个 AppleConfig 对象,那么你就可以这么去使用上述定义的接口:

        IConfigLoader<AppleConfig> loader = new AppleConfigLoader<AppleConfig>();
        AppleConfig config = loader.load();

    于是你将配置文件里的信息转化成了一个AppleConfig对象,并且你能得到这个AppleConfig对象实例。

    到目前,貌似只要我们的 AppleConfigLoader 里面实现了怎么加载配置文件的具体劳动,我们就可以轻易加载配置了。

    可以这么说,但是不是还没有考虑到,配置可能通过不同的方式加载呢,比如通过Properties加载,通过dom方式加载,通过sax方式加载,或者通过某些第三方的开源库来加载。

    因此,除了 配置加载器 ,我们还需要另外一种角色,配置加载方式的提供者。暂且,我们就叫它IConfigProvider。

    配置加载方式的提供者

    配置加载方式的提供者可以提供一种加载方式给配置加载器,换言之,提供一个 对象 给配置加载器。

    如果通过dom方式加载,那么 提供者 提供一个 Document 对象给 加载器 。 如果通过Properties方式加载,那么 提供者 提供一个 Properties 对象给 加载器 如果通过第三方类库提供的方式加载,比如apache-commons-digester3(tomcat的配置加载),那么 提供者 提供一个 Digester 对象给 加载器

    提供者的职责就是 提供 ,仅此而已,只提供配置加载器所需要的对象,但它本身并不参与配置加载的劳动。

    我们用一个接口 IConfigProvider 来定义这个 提供者

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 上午11:54:28
     * @version 1.0
     *
     */
    public interface IConfigProvider<T> {
    
        /**
         * provide a config source used for loading config
         *
         * @return
         * @throws ConfigException
         */
        public T provide() throws ConfigException;
    }

    这里为什么又会有 <T> 来声明泛型呢?

    如果需要一个提供者,那么至少得告诉这个提供者它该提供什么吧。

    因此,一个提供者会提供什么,由这个来决定。

    同时,到这里,我们可以先建造一个工厂,让它来生产特定的提供者:

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 上午11:56:28
     * @version 1.0
     *
     */
    public class ConfigProviderFactory {
    
        private ConfigProviderFactory() {
            throw new UnsupportedOperationException("Unable to initialize a factory class : "
                    + getClass().getSimpleName());
        }
    
        public static IConfigProvider<Document> createDocumentProvider(String filePath) {
            return new DocumentProvider(filePath);
        }
    
        public static IConfigProvider<Properties> createPropertiesProvider(String filePath) {
            return new PropertiesProvider(filePath);
        }
    
        public static IConfigProvider<Digester> createDigesterProvider(String filePath) {
                return new DigesterProvider(filePath);
        }
    }

    可以开始实现具体配置加载器了?

    还不行!

    到这里,假设我们有一个配置文件,叫apple.xml。而且我们要通过DOM方式把这一份apple.xml加载后变成AppleConfig对象。

    那么,首先我要通过提供者工厂给我制造一个能提供Document的提供者。然后拿到这个提供者,我就可以调用它的provide方法来获得Document对象,有了document对象,那么我就可以开始来加载配置了。

    可是,如果要加载BananaConfig、PearConfig…….呢,其步骤都是一样的。因此我们还要有一个抽象类,来实现一些默认的共同行为。

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 上午11:59:19
     * @version 1.0
     *
     */
    public abstract class AbstractConfigLoader <T, U> implements IConfigLoader<T>{
    
        protected IConfigProvider<U> provider;
    
        protected AbstractConfigLoader(IConfigProvider<U> provider) {
            this.provider = provider;
        }
    
        /*
         * @see IConfigLoader#load()
         */
        @Override
        public T load() throws ConfigException {
            return load(getProvider().provide());
        }
    
        public abstract T load(U loaderSource) throws ConfigException;
    
        protected IConfigProvider<U> getProvider() {
            return this.provider;
        }
    }

    每个配置加载器都有一个带参数构造器,接收一个Provider。

    泛型指明了我要加载的是AppleConfig还是BananConfig,泛型 <U> 指明了要用什么加载方式加载,是Document呢,还是Properties,或者其他。

    实战运用实例

    有一份菜市场配置文件market.xml,配置了菜市场的商品,里面有两种商品,分别是苹果和鸡蛋。

    <market>
        <apple>
            <color>red</color>
            <price>100</price>
        </apple>
        <egg>
            <weight>200</weight>
        </egg>
    </market>

    另外还有一份关于各个档口老板名字的配置文件,owner.properties

    port1=Steve Jobs
    port2=Bill Gates
    port3=Kobe Bryant

    我们先定义好如下类:MarketConfig.java

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:03:37
     * @version 1.0
     *
     */
    public class MarketConfig {
    
        private AppleConfig appleConfig;
        private EggConfig eggConfig;
        private OwnerConfig ownerConfig;
    
        public AppleConfig getAppleConfig() {
            return appleConfig;
        }
        public void setAppleConfig(AppleConfig appleConfig) {
            this.appleConfig = appleConfig;
        }
        public EggConfig getEggConfig() {
            return eggConfig;
        }
        public void setEggConfig(EggConfig eggConfig) {
            this.eggConfig = eggConfig;
        }
        public OwnerConfig getOwnerConfig() {
            return ownerConfig;
        }
        public void setOwnerConfig(OwnerConfig ownerConfig) {
            this.ownerConfig = ownerConfig;
        }
    }

    AppleConfig.java

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:03:45
     * @version 1.0
     *
     */
    public class AppleConfig {
    
        private int price;
        private String color;
    
        public void setPrice(int price) {
            this.price = price;
        }
    
        public int getPrice() {
            return this.price;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public String getColor() {
            return this.color;
        }
    }

    EggConfig.java

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:03:58
     * @version 1.0
     *
     */
    public class EggConfig {
    
        private int weight;
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
        public int getWeight() {
            return this.weight;
        }
    }

    OwnerConfig.java

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:04:06
     * @version 1.0
     *
     */
    public class OwnerConfig {
    
        private Map<String, String> owner = new HashMap<String, String>();
    
        public void addOwner(String portName, String owner) {
            this.owner.put(portName, owner);
        }
    
        public String getOwnerByPortName(String portName) {
            return this.owner.get(portName);
        }
    
        public Map<String, String> getOwners() {
            return Collections.unmodifiableMap(this.owner);
        }
    }

    这个例子有两种配置加载方式,分别是Dom和Properties加载方式。

    所以我们的提供者建造工厂需要制造两种提供者provider.

    而且需要定义2个配置加载器,分别是:

    OwnerConfigLoader

    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:24:50
     * @version 1.0
     *
     */
    public class OwnerConfigLoader extends AbstractConfigLoader<OwnerConfig, Properties>{
    
        /**
         * @param provider
         */
        protected OwnerConfigLoader(IConfigProvider<Properties> provider) {
            super(provider);
        }
    
        /* 
         * @see AbstractConfigLoader#load(java.lang.Object)
         */
        @Override
        public OwnerConfig load(Properties props) throws ConfigException {
            OwnerConfig ownerConfig = new OwnerConfig();
    
            /**
             * 利用props,设置ownerConfig的属性值
             * 
             * 此处代码省略
             */
            return ownerConfig;
        }
    }

    然后是MarketConfigLoader

    import org.w3c.dom.Document;
    
    /**
     *
     *
     * @author Bean
     * @date 2016年1月21日 下午11:18:56
     * @version 1.0
     *
     */
    public class MarketConfigLoader extends AbstractConfigLoader<MarketConfig, Document> {
    
        /**
         * @param provider
         */
        protected MarketConfigLoader(IConfigProvider<Document> provider) {
            super(provider);
        }
    
        /* 
         * AbstractConfigLoader#load(java.lang.Object)
         */
        @Override
        public MarketConfig load(Document document) throws ConfigException {
    
            MarketConfig marketConfig = new MarketConfig();
            AppleConfig appleConfig = new AppleConfig();
            EggConfig eggConfig = new EggConfig();
            /**
             * 在这里处理document,然后就能得到
             * AppleConfig和EggConfg
             * 
             * 此处代码省略
             */
            marketConfig.setAppleConfig(appleConfig);
            marketConfig.setEggConfig(eggConfig);
    
            /**
             * 由于OwnerConfig是需要properties方式来加载,不是xml
             * 所以这里要新建一个OwnerConfigLoader,委托它来加载OwnerConfig
             */
    
            OwnerConfigLoader ownerConfigLoader = new OwnerConfigLoader(ConfigProviderFactory.createPropertiesProvider(YOUR_FILE_PATH));
            OwnerConfig ownerConfig = ownerConfigLoader.load();
    
            marketConfig.setOwnerConfig(ownerConfig);
    
            return marketConfig;
        }
    }

    然后,我们在应用层面如何获取到MarketConfig呢

    MarketConfigLoader marketConfigLoader = new MarketConfigLoader(ConfigProviderFactory.createDocumentProvider(YOUR_FILE_PATH));
    MarketConfig marketConfig = marketConfigLoader.load();

    也许有个地方会人奇怪,明明有四个配置类,为什么只有2个配置加载器呢。因为MarketConfig、EggConfig和AppleConfig,都是从同一个xml配置文件里面加载,所以只要一个Document对象,通过MarketConfigLoader就可以全部加载。

    而OwnerConfig是不同的加载方式,所以需要另外一个加载器。

    尾声

    本文提出的配置加载机制,并不能够实际帮忙加载配置,这事应该留给DOM,SAX,以及其他一些开源库如dom4j,Digester去做。但本文提出的配置加载机制能够让配置加载机制更灵活,容易扩展,并且能够集成多种配置加载方式,融合到一个机制进来,发挥各自有点。

    实际上,有些软件经常需要同时从多种不同格式的配置文件里面加载配置,例如struts2,以及我最近在研究并被气到吐血的某国产开源数据库中间件软件,如果没有一套完整的配置加载机制,那么代码会比较散乱,可维护性不高。容易使人吐血。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。