上文,我们效仿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()手动关闭的资源。比如InputStream ,OutputStream。客户经常忽视关闭资源,其性能结果可想而知。
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的情况下更加便捷、灵活的获取配置信息。