没有Spring你知道如何利用注解读取配置吗

本文地址: https://blog.csdn.net/m0_37595562/article/details/81638486
本文源码: https://github.com/zsunny6658/conf

前言:

大家都知道Spring中有一个@Value注解,可以将配置文件中的配置注入到类中的变量。在需要使用自定义配置的情况下,这个功能显得很重要。
本文主要实现了一个类似@Value注解,可以将配置文件中的配置项注入到相应的注解所对应的变量中。目前1.0版本相对简陋——称之为demo更为准确,可以读取yaml格式的配置文件,文件名需要固定为application.yml;同时因为没有spring的bean容器的加持,基于反射的原理,目前智能实现将注解加在静态变量上。本项目将持续优化,相关文章也会持续更新,后期考虑首先加入读取多种文件格式的功能。
最后说一下,为什么Sunny要开发这个SDK。Spring中有这个功能,相对比较方便,但是事实上,如果项目用不到Spring,只是为了读取配置而加入堆Spring的依赖显得有些重。而事实上,在很多场景中,需要用到这个功能,目前所知的有部分内容JDK已经包含了一定的支持,比如Properites类,但是缺少一个完整的解决方案,以及对注解的支持。

正式开始

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

  1. 读取配置文件中的内容
  2. 自定义注解
  3. 注解执行器
1. 读取配置内容

读取配置项这块应该算是相对较为简单的一部分内容了。其实真正的实现就是一个ClassPath中文件内容读入的过程。不过需要注意的一点是,这里如果涉及多种文件格式的读入,需要用到一点设计模式的内容,大家觉得应该用什么设计模式较好呢?可以留言哦。
此处,我们就简单考虑一下yaml格式的文件读入。我们用到了snakeyaml的Yaml类,这个类可以帮助我们解析yaml格式,也避免了我们在这部分重复造轮子——今后提供对properties文件的支持,也将采用Properties类进行读入。
核心的读入代码为:

public static Object loadProperties(String path) throws IOException {

        Yaml yaml = new Yaml();

        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 "";

}

熟悉自定义注解的童鞋,读起来应该没什么难度。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) {
            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("\\.");
                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);
                    field.set(field,String.valueOf(o));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这个方法实现了获得一个类,将类中的成员变量遍历,找出标记了ConfValue注解的成员变量,同时检查是否为静态变量——因为无法获得实例,因此只能通过反射给类变量赋值。随后便是一个while循环,用于找出具体的配置项的值,之前也说过了,loadProperties方法返回的是一个嵌套Map,因此类似“server.port”之类的配置需要多次循环找到配置的值,通常配置是几层,循环需要几次。每一层,都将获得的对象强制转换成Map,并获得他相对应的key的value。最后则是通过反射赋值。
细心的童鞋,可能发现了,还有一部分内容这里没有提到,那就是包扫描。对整个包进行扫描,获得类名,遍历所有类,然后调用上面提到的putInConf方法将配置内容注入修饰了注解的类变量。这块内容,主要还是通过class文件扫描来完成,通过文件扫描来获得具体的类名集合,并循环调用putInConf方法。

后记

至此,1.0版本或者说demo就完成了。虽然相对非常简单,但是核心基本是这些内容,后期主要考虑几个方面优化。首先增加功能,尽量可以做到@Value注解的全部功能,包括但不限于对xml和properties格式的支持,以及对application-{env}.XXX的支持。其次考虑是否可以做到实例变量也能支持,需要找到一个较好的用户体验的方案。最后,考虑对整体设计进行优化,包括代码重构。
相关文章会持续更新发布到此博客中,欢迎大家关注及时跟进整个项目流程,如果有感兴趣的朋友想参与进来也可以联系我。另外,此项目的源码也将持续更新代码,欢迎大家关注。
转载请注明本文地址https://blog.csdn.net/m0_37595562/article/details/81638486
有任何问题也欢迎通过邮箱联系我。

邮箱:zsunny@yeah.net

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值