美文网首页互联网科技程序员技术交流
没有Spring你知道如何利用注解读取配置吗

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

作者: 程序员Sunny | 来源:发表于2018-08-12 21:25 被阅读60次
    本文地址: https://www.jianshu.com/p/7fc0830dd451
    本文源码: 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://www.jianshu.com/p/7fc0830dd451
    有任何问题也欢迎通过邮箱联系我。

    邮箱:zsunny@yeah.net

    相关文章

      网友评论

      本文标题:没有Spring你知道如何利用注解读取配置吗

      本文链接:https://www.haomeiwen.com/subject/ackebftx.html