• 读取配置文件随想


       无论项目大小,数据库、开发环境、测试环境....这些信息,肯定不能在程序中硬编码。

       硬编码有百坏而无一利,每次信息变动都要重新编译项目,不能分离运维和开发。

       而且配置散落在项目的程序中,无法做到准确集中管理,拖慢项目进度。

       我想这也是配置文件出现的原因,配置文件比较主流的格式 properties(键值对形式)、xml(对象,复杂数据结构,只有你想不到没有xml 表达不了的) 等。

       本文已比较常见、简单的 properties 格式的配置文件为例,来看看读取配置文件几种不同的姿势,关注其中的实现和使用,设计模式另表。

    1. Spring 和 Apache Commons Configuration

       如果项目中没什么特殊的个性化读取配置文件需求,可以使用 Spring 管理配置文件信息,然后注入到需要的地方。

       配置文件中需要添加(PS :多配置文件,添加 ignore-unresolvable 参数)。

        <context:property-placeholder location="classpath:db-info.properties" ignore-unresolvable="true"/>
    
        <context:property-placeholder location="classpath:web.properties" ignore-unresolvable="true"/>

       然后在后端服务器需要的地方:

        @Value("${uploadpath}")
        protected String uploadPath;

       动态读入可以使用 Spring 提供了默认的配置文件读取实现类  org.springframework.core.io.DefaultResourceLoader。

       当然你也可以实现  org.springframework.core.io.ResourceLoader 接口自定义配置文件载入实现类。

       org.springframework.core.io.DefaultResourceLoader 核心方法 getResource:

    public Resource getResource(String location) {
            Assert.notNull(location, "Location must not be null");
            Iterator ex = this.protocolResolvers.iterator();
            Resource resource;
            do {
                if(!ex.hasNext()) {
                    if(location.startsWith("/")) {
                        return this.getResourceByPath(location);
                    }
                    if(location.startsWith("classpath:")) {
                        return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
                    }
                    try {
                        URL ex1 = new URL(location);
                        return new UrlResource(ex1);
                    } catch (MalformedURLException var5) {
                        return this.getResourceByPath(location);
                    }
                }
                ProtocolResolver protocolResolver = (ProtocolResolver)ex.next();
                resource = protocolResolver.resolve(location, this);
            } while(resource == null);
            return resource;
        }

       可以看出 Spring 能支持入参路径的很多方式,包括已 " /"、"classpath" 开头。 

       如果你想在项目中使用 Spring 提供的默认配置文件载入实现,可以这样书写你的代码。

                ResourceLoader resourceLoader = new DefaultResourceLoader();
                Resource resource = resourceLoader.getResource("log4j.properties");
                Properties props = new Properties();
                props.load(resource.getInputStream());

       当然你也可以引入 Apache Commons Configuration jar 内部设计相当考究。

       整个 jar 不超过 400K,如果时间充裕,你也可以反编译看看源码。

       使用方式也特别简洁,两行代码就 OK:

      PropertiesConfiguration configuration  = new PropertiesConfiguration("log4j.properties");
      configuration.getString("log4j.appender.file");

       Apache Commons Configuration 默认载入配置文件核心实现类 org.apache.commons.configuration.AbstractFileConfiguration 载入方法:

        public void load(String fileName) throws ConfigurationException {
            try {
                URL e = ConfigurationUtils.locate(this.fileSystem, this.basePath, fileName);
                if(e == null) {
                    throw new ConfigurationException("Cannot locate configuration source " + fileName);
                } else {
                    this.load(e);
                }
            } catch (ConfigurationException var3) {
                throw var3;
            } catch (Exception var4) {
                throw new ConfigurationException("Unable to load the configuration file " + fileName, var4);
            }
        }

    2. JDK 经典手写

       如果你项目对读取配置文件没有太多个性化的需求,如果你有足够时间,如果你嫌弃第三方 Jar 占据你 lib 目录的一席之地,还有如果你热爱编程。

       仔细一点,你会发现,这些开源框架底层都是已 java.net.URL 载入配置文件。

       在载入配置文件的过程中应项目需求采用了恰当的设计模式,使能够支持一些对配置文件的特定操作。

       载入文件后,实例化为 java.util.Properties 对象,进行配置文件获取。

       那就完全可以撸段纯 JDK 的写法,作为工具类放入项目中,编译后不超过 5K,核心的几句代码如下:

              URL resource = Thread.currentThread().getContextClassLoader().getResource("log4j.properties");
              Properties properties = new Properties();
              properties.load(resource.openStream());
              properties.getProperty(key);

      因为  java.util.Properties 的 load 进行了方法的重载,你也可以不用 URL 方式读取配置文件,也可以这样写:

              String url = this.getClass().getClassLoader().getResource("").getPath().replaceAll("%20", " ");
              String path = url.substring(0, url.indexOf("classes")) + filePath; //该 path 为你配置文件的路径
    
              InputStream inputStream = new FileInputStream(path);
              BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
              prop.load(bufferedReader);prop.getProperty(key);

     上述代码都为实例核心的几句代码,其中的判空和异常都没有进行处理,仅作为参考。

     最后贴上自己封装的配置文件载入类,不使用任何第三方 jar,有需要的拿走放入项目即可用。

    package com.rambo.sme.util;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.NoSuchElementException;
    import java.util.Properties;
    
    /**
     * Properties文件载入工具类. 可载入多个properties文件
     * 同一属性在最后载入的文件中的值将会覆盖之前的值,以System的Property优先.
     */
    public class PropertiesLoader {
    
        private final Properties properties;
    
        public PropertiesLoader(String... resourcesPaths) {
            properties = loadProperties(resourcesPaths);
        }
    
        public Properties getProperties() {
            return properties;
        }
    
        /**
         * 取出String类型的Property,但以System的Property优先,如果都为Null则抛出异常.
         */
        public String getProperty(String key) {
            String value = getValue(key);
            if (value == null) {
                throw new NoSuchElementException();
            }
            return value;
        }
    
        /**
         * 取出String类型的Property,但以System的Property优先.如果都为Null则返回Default值.
         */
        public String getProperty(String key, String defaultValue) {
            String value = getValue(key);
            return value != null ? value : defaultValue;
        }
    
        /**
         * 取出Integer类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常.
         */
        public Integer getInteger(String key) {
            String value = getValue(key);
            if (value == null) {
                throw new NoSuchElementException();
            }
            return Integer.valueOf(value);
        }
    
        /**
         * 取出Integer类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常
         */
        public Integer getInteger(String key, Integer defaultValue) {
            String value = getValue(key);
            return value != null ? Integer.valueOf(value) : defaultValue;
        }
    
        /**
         * 取出Double类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常.
         */
        public Double getDouble(String key) {
            String value = getValue(key);
            if (value == null) {
                throw new NoSuchElementException();
            }
            return Double.valueOf(value);
        }
    
        /**
         * 取出Double类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常
         */
        public Double getDouble(String key, Integer defaultValue) {
            String value = getValue(key);
            return value != null ? Double.valueOf(value) : new Double(defaultValue);
        }
    
        /**
         * 取出Boolean类型的Property,但以System的Property优先.如果都为Null抛出异常,如果内容不是true/false则返回false.
         */
        public Boolean getBoolean(String key) {
            String value = getValue(key);
            if (value == null) {
                throw new NoSuchElementException();
            }
            return Boolean.valueOf(value);
        }
    
        /**
         * 取出Boolean类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容不为true/false则返回false.
         */
        public Boolean getBoolean(String key, boolean defaultValue) {
            String value = getValue(key);
            return value != null ? Boolean.valueOf(value) : defaultValue;
        }
    
        /**
         * 取出Property,但以System的Property优先,取不到返回空字符串.
         */
        private String getValue(String key) {
            String systemProperty = System.getProperty(key);
            if (systemProperty != null) {
                return systemProperty;
            }
            if (properties.containsKey(key)) {
                return properties.getProperty(key);
            }
            return "";
        }
    
        /**
         * 载入多个文件, 文件路径使用Spring Resource格式.
         */
        private Properties loadProperties(String... resourcesPaths) {
            Properties props = new Properties();
    
            for (String location : resourcesPaths) {
                try {
                    URL url = Thread.currentThread().getContextClassLoader().getResource(location);
                    if(url != null){
                        props.load(url.openStream());
                    }
                } catch (IOException ex) {
                    System.out.println("Could not load properties from path:" + location + ", " + ex.getMessage());
                }
            }
            return props;
        }
    }
    View Code

      使用方式也很简单,支持多路径读入,如果存在相同 Key 后面的覆盖前面的:

            PropertiesLoader propertiesLoader = new PropertiesLoader("log4j.properties");
            System.out.println(propertiesLoader.getProperty("log4j.appender.file"));
  • 相关阅读:
    机器学习笔记(一)线性回归模型
    为什么拷贝构造函数的参数可以直接去访问它自己的私有成员?
    C++之forward move源码分析
    C++之forward
    C++之不完整的数据类型释放
    C++中typename关键字的使用方法和注意事项(好文收藏)
    C++之左值引用和右值引用
    C++之Class内存
    C++ 之Const
    C++之DISALLOW_COPY_AND_ASSIGN
  • 原文地址:https://www.cnblogs.com/java-class/p/5786473.html
Copyright © 2020-2023  润新知