美文网首页
Java通过反射注解赋值

Java通过反射注解赋值

作者: 小码A梦 | 来源:发表于2022-07-21 09:08 被阅读0次

    前段时间,领导分配一个统计销售区域汇总的数据,解决方案使用到了反射获取注解,通过注解获取属性或者设置字段属性。

    问题描述

    查询公司列表,分别是公司id、区域id、区域名称:

    公司id 区域id 区域名称
    1 1 华南
    2 2 华北
    3 2 华北
    4 3 华东
    5 3 华东

    创建公司类Company

    public class Company {
    
        public Company(Integer id,  Integer areaId, String areaName) {
            this.id = id;
            this.areaId = areaId;
            this.areaName = areaName;
        }
    
        /**
         * 公司id
         */
        private Integer id;
    
        /**
         * 区域id
         */
        private Integer areaId;
    
        /**
         * 区域名称
         */
        private String areaName;
         
        // 省略get/set方法 
    
    }
    

    最终解决

    要求汇总各个区域公司数量,得到如下汇总:

    区域id 区域名称 公司总数
    1 华南 1
    2 华北 2
    3 华东 2

    最终区域实体AreaStatistic:

    public class AreaStatistic {
    
        @ColumnProperty("华东大区")
        private Integer eastChina = 0;
    
        @ColumnProperty("华东id")
        private Integer eastChinaId;
    
        @ColumnProperty("华南大区")
        private Integer southChina = 0;
    
        @ColumnProperty("华南id")
        private Integer southChinaId;
    
        @ColumnProperty("华北大区")
        private Integer northChina = 0;
    
        @ColumnProperty("华北id")
        private Integer northChinaId;
    
        @Override
        public String toString() {
            return "AreaStatistic{\n" +
                    "华东Id=" + eastChinaId +
                    ",华东=" + eastChina +
                    ", \n华南Id=" + southChinaId +
                    ", 华南=" + southChina +
                    ", \n华北Id=" + northChinaId +
                    ", 华北=" + northChina +
                    '}';
        }
        // 省略get/set方法
    }
    

    if/else 普通解法

    AreaStatistic areaStatistic = new AreaStatistic();
    for (Company company:companyList) {
        String areaName = company.getAreaName();
        if ("华南".equals(areaName)) {
            areaStatistic.setSouthChina(areaStatistic.getSouthChina()+1);
            areaStatistic.setSouthChinaId(company.getAreaId());
        } else if ("华北".equals(areaName)) {
            areaStatistic.setNorthChina(areaStatistic.getNorthChina()+1);
            areaStatistic.setNorthChinaId(company.getAreaId());
        } else if ("华东".equals(areaName)) {
            areaStatistic.setEastChina(areaStatistic.getEastChina()+1);
            areaStatistic.setEastChinaId(company.getAreaId());
        }
    }
    

    输出:

    华东Id=3,华东=2, 
    华南Id=1, 华南=1, 
    华北Id=2, 华北=2
    

    这种做法的缺点:

    • 要写大量的条件判断语句,非常的繁琐。
    • 增加和减少统计区域,都要修改代码。

    针对上面的缺点,使用反射获取注解,通过注解获取属性赋值。

    通过反射注解赋值属性

    解题思路

    1. 遍历公司列表,获取到区域id和区域名称。
    2. 创建自定义注解@ColumnProperty:
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ColumnProperty {
    
        String value() default "";
    
    }
    
    1. 通过反射获取属性,然后遍历字段属性获取注解。

    AreaStatistic字段属性上添加注解:

    @ColumnProperty("华东大区")
    private Integer eastChina = 0;
    
    @ColumnProperty("华东id")
    private Integer eastChinaId;
    
    @ColumnProperty("华南大区")
    private Integer southChina = 0;
    
    @ColumnProperty("华南id")
    private Integer southChinaId;
    
    @ColumnProperty("华北大区")
    private Integer northChina = 0;
    
    @ColumnProperty("华北id")
    private Integer northChinaId;
    
    1. 通过反射获取属性,然后遍历字段属性获取注解。
    Class staticClass = areaStatistic.getClass();
    Field[] fields = staticClass.getDeclaredFields();
    for (Field field : fields) {
        ColumnProperty property = field.getAnnotation(ColumnProperty.class);
        String value = property.value();
    }
    
    1. 匹配区域名称和字段属性,比如遍历公司区域是华东,就遍历到华东大区注解对应的字段,并赋值或者获取字段值。
    if (value != null) {
        int indexOf = value.indexOf("大区");
        if (indexOf != -1 && value.length() == 4) {
            if (areaName.equals(value.substring(0,2))) {
                field.setAccessible(true);
                field.set(areaStatistic,(Integer) field.get(areaStatistic) + 1);  
            }
         }
    }
    
    1. 区域id赋值也是相同的解题思路。

    根据上面的思路,有如下代码汇总

    // 遍历公司
    for (Company company:companyList) {
      setAreaProperty(areaStatistic2,company.getAreaName(),company.getAreaId());
    }
    
    private void setAreaProperty(AreaStatistic areaStatistic,String areaName,Integer areaId) throws IllegalAccessException {
        // 反射获取注解 
        Class staticClass = areaStatistic.getClass();
        Field[] fields = staticClass.getDeclaredFields();
        for (Field field : fields) {
            ColumnProperty property = field.getAnnotation(ColumnProperty.class);
            String value = property.value();
            if (value != null) {
                int indexOf = value.indexOf("大区");
                if (indexOf != -1 && value.length() == 4) {
                    // 匹配到注解属性并赋值 
                    if (areaName.equals(value.substring(0,2))) {
                        field.setAccessible(true);
                        field.set(areaStatistic,(Integer) field.get(areaStatistic) + 1);
                        for (Field idField : fields) {
                            ColumnProperty idProperty = idField.getAnnotation(ColumnProperty.class);
                            String idValue = idProperty.value();
                            if (idValue.equals(areaName+"id")) {
                                idField.setAccessible(true);
                                idField.set(areaStatistic,areaId);
                                break;
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
    

    输出:

    华东Id=3,华东=2, 
    华南Id=1, 华南=1, 
    华北Id=2, 华北=2
    

    汇总某些字段的和

    上面算出各个区域的汇总之后,还要算出全部区域的总和,这里还是使用到注解,把属性字段包含大区都累加起来:

    AreaStatistic statistic = new AreaStatistic();
    statistic.setEastChina(2);
    statistic.setNorthChina(3);
    statistic.setSouthChina(1);
    int sum = 0;
    Class staticClass = statistic.getClass();
    Field[] fields = staticClass.getDeclaredFields();
    for (Field field : fields) {
        ColumnProperty property = field.getAnnotation(ColumnProperty.class);
        String value = property.value();
        if (value.indexOf("大区") != -1) {
            field.setAccessible(true);
            sum += field.get(statistic) == null ? 0 : (Integer) field.get(statistic);
        }
    }
    System.out.println(sum);
    

    输出结果:

    6
    

    总结

    • 自定义注解,通过反射获取注解
    • 通过匹配注解值,获取或者复制对应的字段属性。

    赋值主要代码为:

    field.setAccessible(true);
    field.set(Model,value);
    

    源码地址

    https://github.com/jeremylai7/java-codes/blob/master/basis/src/main/java/reflect/SetValueByAnnotation.java

    相关文章

      网友评论

          本文标题:Java通过反射注解赋值

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