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

问题描述

查询公司列表,分别是公司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通过反射注解赋值的更多相关文章

  1. 【Java EE 学习 24 下】【注解在数据库开发中的使用】【反射+注解+动态代理在事务中的应用service层】

    一.使用注解可以解决JavaBean和数据库中表名不一致.字段名不一致.字段数量不一致的问题. 1.Sun公司给jdbc提供的注解 @Table.@Column.@Id.@OneToMany.@One ...

  2. java反射--注解的定义与运用以及权限拦截

    自定义注解类编写的一些规则: 1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是 ...

  3. 【译】8. Java反射——注解

    原文地址:http://tutorials.jenkov.com/java-reflection/annotations.html ================================== ...

  4. Java学习:注解,反射,动态编译

    狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! Java学习:注解,反射,动态编译 Annotation 注解  什么是注解 ? Annotat ...

  5. Java的反射和代理以及注解

    最近接触到java的反射和代理(接触的有点迟了...),还是有必要总结下 1. Java的反射 有的时候我们需要在程序运行的时候获取类.方法等信息用于动态运行,这个时候反射就能够帮我们找到类.方法.成 ...

  6. java中的注解(Annotation)

    转载:https://segmentfault.com/a/1190000007623013 简介 注解,java中提供了一种原程序中的元素关联任何信息.任何元素的途径的途径和方法. 注解是那些插入到 ...

  7. java之反射的基本介绍

    什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的以及动态调用对象的方法的功能称为Java的反射 ...

  8. Java中的注解基础

    一.元注解 元注解的作用就是负责注解其他注解. 1.@Target @Target用来指明注解所修饰的目标,包括packages.types(类.接口.枚举.Annotation类型).类型成员(方法 ...

  9. java中的注解详解和自定义注解

    一.java中的注解详解 1.什么是注解 用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数据.比如,下面这段代码: @Override public Str ...

随机推荐

  1. mybaitis查询 (数据库与实体类字段名不相同)

    1.这是我的数据库字段名和实体类字段名 2.方法 方法一: 查询的结果标题 会跟实体类的属性一一匹配,一定要一致就算数据库字段和属性不一致,我们可以把查询结果设置一个别名,让别名=属性名 方法二:使用 ...

  2. Java注解Annotaton

    1.三种基本的Annotaton @Override : 限定某个方法,是重写父类方法 , 该注解只能用于方法 @Deprecated : 用于表示某个程序元素 ( 类 , 方法等 ) 已过时 @Su ...

  3. Typora 开始收费,改用好玩的MarkText

    收费-- 可以考虑使用:MarkText 简述MarkText MarkText 这个工具侧重于"命令",导航栏都被收起来了.有些小伙伴感觉反而不好用,其实不然,是未了解该工具的强 ...

  4. VS.NET启动显示ID为XXXX的进程当前未运行

    解决办法:在启动项目根目录下用文本编辑器打开Web项目下的{X}.csproj文件,然后查找 <WebProjectProperties>,将这一对标签之间的内容全部删除,然后再打开项目就 ...

  5. ptorch常用代码梯度篇(梯度裁剪、梯度累积、冻结预训练层等)

    梯度裁剪(Gradient Clipping) 在训练比较深或者循环神经网络模型的过程中,我们有可能发生梯度爆炸的情况,这样会导致我们模型训练无法收敛. 我们可以采取一个简单的策略来避免梯度的爆炸,那 ...

  6. MySQLDocker 主从复制搭建

    MySQLDocker 主从复制搭建 MySQLDocker 的搭建 docker search mysql docker pull mysql/mysql-server:8.0.26 docker ...

  7. React项目实现导出PDF的功能

    在做web项目中,有时候会遇到pdf导出的需求,现根据之前在公司的React项目中遇到的导出PDF需求,整理一个demo出来. 导出PDF需要用到两个依赖包:html2canvas.jspdf 1.安 ...

  8. SpringBoot整合RabbitMQ实战附加死信交换机

    前言 使用springboot,实现以下功能,有两个队列1.2,往里面发送消息,如果处理失败发生异常,可以重试3次,重试3次均失败,那么就将消息发送到死信队列进行统一处理,例如记录数据库.报警等 环境 ...

  9. 1.设计模式第一步-《设计模式从头到脚舔一遍-使用C#实现》

    更新记录: 完成第一次编辑:2022年4月23日20:29:33. 加入小黄人歌曲:2022年4月23日21:45:36. 1.1 设计模式(Design Pattern)是什么 设计模式是理论.是前 ...

  10. django--ORM表的多对一关系

    多对一关系是什么 Django使用django.db.models.ForeignKey定义多对一关系. ForeignKey需要一个位置参数:与该模型关联的类  class Info(models. ...