当前位置: 主页 > JAVA语言

java读取properties值-Spring中文件内容读入的设计模式较好呢?

发布时间:2023-06-15 22:05   浏览次数:次   作者:佚名

前言:

大家都知道Spring中有一个@Value注解,可以将配置文件中的配置注入到类中的变量。在需要使用自定义配置的情况下,这个功能显得很重要。

本文主要实现了一个类似@Value注解,可以将配置文件中的配置项注入到相应的注解所对应的变量中。目前1.0版本相对简陋——称之为demo更为准确,可以读取yaml格式的配置文件,文件名需要固定为application.yml;同时因为没有spring的bean容器的加持,基于反射的原理,目前智能实现将注解加在静态变量上。本项目将持续优化,相关文章也会持续更新,后期考虑首先加入读取多种文件格式的功能。

最后说一下,为什么Sunny要开发这个SDK。Spring中有这个功能,相对比较方便,但是事实上,如果项目用不到Spring,只是为了读取配置而加入堆Spring的依赖显得有些重。而事实上,在很多场景中,需要用到这个功能,目前所知的有部分内容JDK已经包含了一定的支持,比如Properites类,但是缺少一个完整的解决方案,以及对注解的支持。

正式开始

要完成一个类似的功能,首先要考虑几个方面。本文也将从以下几个方面来详细说说。

1. 读取配置内容

读取配置项这块应该算是相对较为简单的一部分内容了。其实真正的实现就是一个ClassPath中文件内容读入的过程。不过需要注意的一点是,这里如果涉及多种文件格式的读入,需要用到一点设计模式的内容,大家觉得应该用什么设计模式较好呢?可以留言哦。

此处,我们就简单考虑一下yaml格式的文件读入。我们用到了snakeyaml的Yaml类,这个类可以帮助我们解析yaml格式,也避免了我们在这部分重复造轮子——今后提供对properties文件的支持,也将采用Properties类进行读入。

核心的读入代码为:

public static Object loadProperties(String path) throws IOException {

Yaml yaml = new Yaml();

java读取properties值_java读取properties值_java properties 中的值 变量

Object o = yaml.load(readFile(path));

return o;

}

非常简单,这里入参就是一个path作为配置文件在classpath下的文件路径,readFile则是Sunny实现的一个从classpath中读取文件的方法,会返回读取文件的内容转换成String。此处,loadProperties将返回一个Object对象,对象中其实包含了一个配置树——具体可以看成一个嵌套Map。

2. 自定义注解

自定义注解也是一个相对较为简单的部分,话不多说,直接上代码:

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface ConfPath {

public String value() default "";

java读取properties值_java读取properties值_java properties 中的值 变量

}

熟悉自定义注解的童鞋,读起来应该没什么难度。Sunny在这里简单说一下,上面三个注解是元注解,通常定义自定义注解用的。@Target注解说明,这个自定义注解是用来修饰什么的,这里就是修饰类成员变量的。@Retention可以看成这个自定义注解的生存周期,此处就是运行时。@Documented表示这个自定义注解会被JavaDoc记录。

3. 注解执行器

注解执行器是整个项目实现中最难的一部分。他主要完成,对整个包的扫描,找出有相应的注解修饰的元素,并从读取的配置项中找到相对应的匹配内容注入。

这部分的核心代码如下:

private static void putInConf(Class clazz){

Field[] fields = clazz.getDeclaredFields();

Object oo = null;

try {

oo = LocalPropertiesUtils.loadProperties("application.yml");

} catch (IOException e) {

java properties 中的值 变量_java读取properties值_java读取properties值

e.printStackTrace();

}

for (Field field : fields) {

if(field.isAnnotationPresent(ConfPath.class)){

//static检查

if((field.getModifiers()&8) == 0){

throw new RuntimeException("配置项必须为static变量");

}

ConfPath confPath = field.getAnnotation(ConfPath.class);

Object o = oo;

String[] props = confPath.value().split("\\.");

java properties 中的值 变量_java读取properties值_java读取properties值

int ind = 0;

while (true){

if(ind < props.length && o instanceof Map){

o = ((Map) o).get(props[ind]);

}else {

break;

}

ind ++;

}

try {

field.setAccessible(true);

java properties 中的值 变量_java读取properties值_java读取properties值

field.set(field,String.valueOf(o));

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

}

}

这个方法实现了获得一个类,将类中的成员变量遍历,找出标记了ConfValue注解的成员变量,同时检查是否为静态变量——因为无法获得实例java读取properties值,因此只能通过反射给类变量赋值。随后便是一个while循环,用于找出具体的配置项的值,之前也说过了,loadProperties方法返回的是一个嵌套Map,因此类似“server.port”之类的配置需要多次循环找到配置的值,通常配置是几层,循环需要几次。每一层,都将获得的对象强制转换成Map,并获得他相对应的key的value。最后则是通过反射赋值。

细心的童鞋,可能发现了,还有一部分内容这里没有提到,那就是包扫描。对整个包进行扫描java读取properties值,获得类名,遍历所有类,然后调用上面提到的putInConf方法将配置内容注入修饰了注解的类变量。这块内容,主要还是通过class文件扫描来完成,通过文件扫描来获得具体的类名集合,并循环调用putInConf方法。

后记

至此,1.0版本或者说demo就完成了。虽然相对非常简单,但是核心基本是这些内容,后期主要考虑几个方面优化。首先增加功能,尽量可以做到@Value注解的全部功能,包括但不限于对xml和properties格式的支持,以及对application-{env}.XXX的支持。其次考虑是否可以做到实例变量也能支持,需要找到一个较好的用户体验的方案。最后,考虑对整体设计进行优化,包括代码重构。

java读取properties值_java读取properties值_java properties 中的值 变量