Java IO获取配置信息的正确姿势

上文,我们效仿Spring源码,完成了解析配置信息的优雅实践,可以覆盖日常开发中绝大多数的情形。但是有时候还是不够灵活,比如要求配置键前缀一致。
通过实现EnvironmentAware接口,或者注入Environment的bean,使用getProperty()虽然可以解决配置键比较离散、没有关联情形下的取值问题。但总要实现接口或者注入bean,不能随心所欲,难以挣脱Spring的桎梏。不免意难平,难道就只能带着镣铐起舞吗?

IO的方式

回归语言的层面上来,Java本身具备IO强大的支持。我们就不能通过IO的方式读取文件,然后解析成键值对的数据结构吗。
读取成流再进行解析,感官上难免觉得十分麻烦,解决问题的同时带来更多新的问题,有悖初衷。其实JDK提供了便捷的Api供大家使用。
ClassLoader除了可以加载类信息之外,读取properties文件也是一把好手。getResourceAsStream()支持将配置文件转化成InputStream
同时解析过程JDK也提供了支持,java.util.Properties类中的load()整好可以使得输入流解析成为键值对。如此这般,可谓天时地利人和。

解析过程

The specified stream remains open after this method returns

这里必须注意,JDK对load()有如上说明: 此方法返回后,指定的流保持打开状态。也就是说我们必须手动关闭流,以免造成内存浪费。

package com.spring.load_properties;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Author: Raphael
 */
public class JavaLoadResources {

    private static final Properties properties;

    static {
        properties = new Properties();
        try (
            InputStream in = ClassLoader.getSystemClassLoader()
                        .getResourceAsStream("value.properties"))
        {
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private JavaLoadResources() {}

    public static JavaLoadResources getIntance() {
        return Loader.INSTANCE.loader;
    }

    public Properties getProperties() {
        return properties;
    }

    private enum Loader {
        INSTANCE;

        private JavaLoadResources loader;

        private Loader() {
            loader = new JavaLoadResources();
        }
    }

}
复制代码

但是看到最后,代码中依然没有调用流的close(),而且为什么try-catch的结构也有差异?其实这里是使用了Java 1.7之后提供的try-with-resources语法糖。Java类库中包含许多必须通过调用close()手动关闭的资源。比如InputStreamOutputStream。客户经常忽视关闭资源,其性能结果可想而知。
Java集合框架之父布洛赫在其著作《Effective-Java》中如此论述:

try-with-resources版本比原始版本更精简,更好的可读性,而且它们提供了更好的诊断。 可以在 try-with-resources 语句中添加 catch 子句,就像在常规的 try-finally 语句中一样。这允许你处理异常,而 不会在另一层嵌套中污染代码。

try-with-resources会在执行完try代码块后自动关闭资源。前提是你必须实现了Closeable接口。ta是保证资源正确关闭的最佳方式。

此外工具类一般设计为单例模式。 这里推荐大家使用声明单一元素的枚举类的方式实现。这也是布洛赫最为赞同的实现方式。《Effective-Java》中如此论述:

这种方式类似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是单一元素枚举类通常是实现单例的最佳方式。
注意,如果单例必须继承 Enum 以外的父类(尽管可以声明一个Enum来实现接口),那么就不能使用这种方法。

测试代码

遍历输出,简单的验证一下即可

package com.spring.load_properties;

import java.util.Properties;
import java.util.function.BiConsumer;

public class GetProperties {

    /**
     * @Author: Raphael
     */
    public static void main(String[] args) {
        JavaLoadResources intance = JavaLoadResources.getIntance();
        Properties properties = intance.getProperties();
        BiConsumer console = (k, v) -> {
            System.out.print(k + ": ");
            System.out.println(v);
        };
        properties.forEach(console);
    }

}

复制代码

value.properties配置文件信息如下:

完全符合预期,这样一来我们便可以在不依赖Spring的情况下更加便捷、灵活的获取配置信息。

猜你喜欢

转载自juejin.im/post/5e3bf77de51d4526e418f081