Java通过反射注解赋值

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

问题描述

查询公司列表,分别是公司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

发表评论

相关文章

当前内容话题