• 手写web服务器:定义@value注解,实现配置自动注入


    前言

    昨天我们定义了Configuration注解和Bean注解,实现了更灵活的类注入,今天我们来看另一个配置注入的注解Value,这个注解也是我们在springboot中经常用到的,今天我们就来看下如何通过value注解实现properties配置的自动注入。

    实现过程

    定义properties工具类

    这个工具类的作用主要是解析我们的配置文件,并生成一个配置文件的字典数据,然后我们可以根据自己的需要获取对应的配置,这也是我们实现配置自动注入的第一步。

    public class PropertiesUtil {
        private static HashMap<String, PropertiesUtil> configMap = new HashMap();
        private Date loadTime = null;
        private ResourceBundle resourceBundle = null;
        private static final Integer TIME_OUT = 60000;
    
        private PropertiesUtil(String name) {
            this.loadTime = new Date();
    
            try {
                this.resourceBundle = ResourceBundle.getBundle(name);
            } catch (Exception var3) {
                this.resourceBundle = null;
            }
    
        }
    
        public static synchronized PropertiesUtil getInstance() {
            return getInstance("application");
        }
    
        public static synchronized PropertiesUtil getInstance(String name) {
            PropertiesUtil conf = configMap.get(name);
            if (null == conf) {
                conf = new PropertiesUtil(name);
                configMap.put(name, conf);
            }
    
            if ((new Date()).getTime() - conf.getLoadTime().getTime() > (long)TIME_OUT) {
                conf = new PropertiesUtil(name);
                configMap.put(name, conf);
            }
    
            return conf;
        }
    
        public String get(String key) {
            try {
                String value = this.resourceBundle.getString(key);
                return value;
            } catch (MissingResourceException var3) {
                return "";
            } catch (NullPointerException var4) {
                return "";
            }
        }
    
        public Integer getInt(String key) {
            try {
                String value = this.resourceBundle.getString(key);
                return Integer.parseInt(value);
            } catch (MissingResourceException var3) {
                return null;
            } catch (NullPointerException var4) {
                return null;
            }
        }
    
        public boolean getBoolean(String key) {
            try {
                String value = this.resourceBundle.getString(key);
                return "true".equals(value);
            } catch (MissingResourceException var3) {
                return false;
            } catch (NullPointerException var4) {
                return false;
            }
        }
    
        public Date getLoadTime() {
            return this.loadTime;
        }
    
        public static String getPropertiesValue(String name, String key) {
            try {
                return getInstance(name).get(key);
            } catch (MissingResourceException var3) {
                return "";
            } catch (NullPointerException var4) {
                return "";
            }
        }
    }
    

    定义value注解

    依然是轻车熟路,这里的value()是用来接受我们的配置名称的。

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Value {
        String value();
    }
    

    实现配置注入

    在之前的实现基础上,我们增加了一个配置处理的类,这个类有两个方法,分别是用于实现单个属性注入和批量属性注入:

    public class ConfigurationHandler {
        private static final PropertiesUtil propertiesUtil = PropertiesUtil.getInstance("application");
    
        /**
         * 初始化value配置信息
         * @param instance
         * @param field
         * @throws IllegalAccessException
         */
        public static void initValueConfig(Object instance, Field field) throws IllegalAccessException {
            Annotation annotation = field.getAnnotation(Value.class);
            if (Objects.nonNull(annotation)) {
                String propertiesKeyName = ((Value) annotation).value();
                Class<?> type = field.getType();
                if (!field.isAccessible()) {
                    field.setAccessible(Boolean.TRUE);
                }
                if (Integer.class.equals(type)) {
                    field.setInt(instance, propertiesUtil.getInt(propertiesKeyName));
                } else if (Boolean.class.equals(type)) {
                    field.setBoolean(instance, propertiesUtil.getBoolean(propertiesKeyName));
                } else {
                    field.set(instance, propertiesUtil.get(propertiesKeyName));
                }
            }
        }
    
        /**
         * 批量初始化value配置
         * @param aClass
         * @param instance
         * @throws IllegalAccessException
         */
        public static void batchInitValueConfig(Class aClass, Object instance) throws IllegalAccessException {
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                initValueConfig(instance, declaredField);
            }
    
        }
    }
    

    首先,我们在IoC实例化阶段,对各种组件的字段进行扫描,拿出有Value注解的属性,根据注解的值获取对应的配置。

    使用效果

    我们在service组件上加入一个属性,并加上value注解:

    然后在我们的配置文件中增加对应的配置:

    运行测试

    运行测试下:

    可以看到,我们的配置已经被注入进来了,这样注入配置,既简单又方便。当然,相比于SpringValue注解,我们的还是显得比较低级,因为springvalue注解是支持表达式的,它有一套专门的Spring EL,所以我们看的springvalue是这样写的:

    @Value("${syske.boot.server.name}")
    private String serverName;
    // 或者这样
    @Value("#{syske.boot.server.name}")
    private String serverName;
    

    好了,今天的内容就这么多,我们接下来总结一下。

    总结

    注解本质上只是一种标记,是为了便于我们通过反射操作类的属性、方法等资源,实现我们的高级功能。value注解就是获取配置的一种标记,我们通过在实例化对象后对其字段操作,实现配置的自动注入。

    在我实际测试的时候,我发现这种配置注入方式,对静态变量也是有效的,但是springvalue对静态变量是无效的,暂时没有去看spring的源码,不知道是实现方式的问题还是EL表达式的锅。

    原本我以为是因为字段和方法的反射操作都是基于类的实例,所以对于静态方法和变量是没有任何效果的,但是经过实测的时候,发现并非如此,后面再好好研究下。

    下面是项目的开源仓库,有兴趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推荐你自己动个手,自己写一下,真的感觉不错:

    https://github.com/Syske/syske-boot
    

  • 相关阅读:
    网址收藏
    Linux创建swap文件
    vim命令大全
    char * 和字符数组
    JSR 203终于要出来啦
    对象关系技术的探讨
    最近编码更流畅了
    孤独终止的地方,就是广场开始的地方......
    不要奢望.NET能够跨平台
    实现了HTTP多线程下载
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/14866479.html
Copyright © 2020-2023  润新知