IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 使用注解+反射来降低代码重复率 -> 正文阅读

[Java知识库]使用注解+反射来降低代码重复率

一、使用场景:

实体类要转化为一串特定的String,且多个成员变量在转化时使用方法一致,若直接对实体类的成员变量一个一个的转义,拼接就显得很麻烦,如果下次再有类似的客户需求还要进行对其他实体信息的拼接,就还要重复一次上边的操作,代码十分臃肿,且重复率高,可复用性也很低。

二、简单的注解+反射拼接实体类信息

1、注解类1——变量名称标签:只有一个value属性,非空

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

?2、注解类2——使用方法名称:只有一个value属性,默认为空

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConvertMethod {
    String value() default "";
}

3、转义工具类——用反射来拼接传入实体信息

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;

public class FieldAndAnnotionUtil {
    private static final String EMPTY = "";

    public static String getLabelAndValue(Object obj) {
        if (Objects.isNull(obj)) {
            return EMPTY;
        }
        //获取反射
        Field[] fields = obj.getClass().getDeclaredFields();
        if (0 == fields.length) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();
        try {
            for (Field field : fields) {
                //授权
                field.setAccessible(true);
                Object value = field.get(obj);
                //获取到的属性为null跳出
                if (null == value) {
                    continue;
                }
                //属性没有添加NameLabel注解跳出
                if (!field.isAnnotationPresent(NameLabel.class)) {
                    continue;
                }
                NameLabel nameLabel = field.getAnnotation(NameLabel.class);
                //属性没有添加ConvertMethod注解跳出,用默认拼接方法
                if (!field.isAnnotationPresent(ConvertMethod.class)) {
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                //获取注解中的方法名,通过反射执行该方法,实现不同变量的不同转义方式
                ConvertMethod methodName = field.getAnnotation(ConvertMethod.class);
                //处理注解方法名为空的情况,依然用默认方法拼接
                if("".equals(methodName.value())){
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                Method method = obj.getClass().getMethod(methodName.value());
                sb.append(nameLabel.value()).append(":").append(method.invoke(obj)).append(",").append(" ");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String str = sb.toString();
        //处理末尾的逗号和空格
        return !"".equals(str) ? str.substring(0, str.length() - 2) : str;
    }
}

4、实体类——使用自定义注解

public class Person {

    @NameLabel(value = "姓名")
    @ConvertMethod("nameLabel")
    private String name;

    @NameLabel(value = "性别")
    @ConvertMethod("sexLabel")
    private String sex;

    @NameLabel(value = "年龄")
    @ConvertMethod("ageLabel")
    private Integer age;

    @NameLabel(value = "身高")
    private Integer height;

    private String occupation;

    public Person(String name, String sex, Integer age, Integer height, String occupation) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
        this.occupation = occupation;
    }

    public String nameLabel() {
        if(name == null || name.trim().equals("")){
            return null;
        }
        return name;
    }

    public String sexLabel() {
        if(sex == null || sex.trim().equals("")){
            return null;
        }
        return sex + "性";
    }

    public String ageLabel() {
        if(age == null){
            return null;
        }
        return age + "岁";
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getHeight() {
        return height;
    }

    public void setHeight(Integer height) {
        this.height = height;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }
}

5、主方法——测试一下

public class FieldAndAnnotionTest {

    public static void main(String[] args) {
        Person person = new Person("张三", "男", 18, 175, "无");
        String personDesc = FieldAndAnnotionUtil.getLabelAndValue(person);
        System.out.println(personDesc);
    }
}

6、执行结果

姓名:张三, 性别:男性, 年龄:18岁, 身高:175

?7、分析:

Person类中未给变量 height 添加 @ConvertMethod 注解,但是转义工具类给了一个默认的注解就是用变量的 @NameLabel 中的名称拼接变量的值。

occupation 变量未给加任何注解,因此在转义中被忽略掉。

因此我们想对类成员变量进行转义的加上,注解,添加特定的实现方法即可。这个地方若是只添加 @ConvertMethod 注解,却没有对应的实现方法是会抛出 java.lang.NoSuchMethodException 异常的。

三、改进版:@NameLabel注解参数添加一个sort属性,让它支持自定义排序

1、改进注解@NameLabel——添加sort属性,默认为0

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NameLabel {
    String value();
    int sort() default 0;
}

2、Person类注解添加自定义顺序

public class Person {

    @NameLabel(value = "姓名")
    @ConvertMethod("nameLabel")
    private String name;

    @NameLabel(value = "性别", sort = 3)
    @ConvertMethod("sexLabel")
    private String sex;

    @NameLabel(value = "年龄", sort = 2)
    @ConvertMethod("ageLabel")
    private Integer age;

    @NameLabel(value = "身高", sort = 4)
    private Integer height;

    private String occupation;

    public Person(String name, String sex, Integer age, Integer height, String occupation) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
        this.occupation = occupation;
    }

    public String nameLabel() {
        if (name == null || name.trim().equals("")) {
            return null;
        }
        return name;
    }

    public String sexLabel() {
        if (sex == null || sex.trim().equals("")) {
            return null;
        }
        return sex + "性";
    }

    public String ageLabel() {
        if (age == null) {
            return null;
        }
        return age + "岁";
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getHeight() {
        return height;
    }

    public void setHeight(Integer height) {
        this.height = height;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }
}

3、修改转义工具类添加根据sort排序方法

public class FieldAndAnnotionUtil {
    private static final String EMPTY = "";

    public static String getLabelAndValue(Object obj) {
        if (Objects.isNull(obj)) {
            return EMPTY;
        }
        //获取反射
        Field[] fields = obj.getClass().getDeclaredFields();
        if (0 == fields.length) {
            return EMPTY;
        }

        List<Field> fieldList = Arrays.stream(fields).
                //获取反射类中添加过@NameLabel注解的属性
                        filter(field -> field.isAnnotationPresent(NameLabel.class)).
                //根据@NameLabel注解的sort值进行分组
                        collect(Collectors.toMap(f -> f.getAnnotation(NameLabel.class).sort(), f -> f))
                //转化后根据sort排序
                .entrySet().stream().
                        sorted(Map.Entry.comparingByKey()).
                //转化为List
                        map(map -> map.getValue()).
                        collect(Collectors.toList());

        StringBuilder sb = new StringBuilder();
        try {
            for (Field field : fieldList) {
                //授权
                field.setAccessible(true);
                Object value = field.get(obj);
                //获取到的属性为null跳出
                if (null == value) {
                    continue;
                }
                //属性没有添加NameLabel注解跳出
                if (!field.isAnnotationPresent(NameLabel.class)) {
                    continue;
                }
                NameLabel nameLabel = field.getAnnotation(NameLabel.class);
                //属性没有添加ConvertMethod注解跳出,用默认拼接方法
                if (!field.isAnnotationPresent(ConvertMethod.class)) {
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                //获取注解中的方法名,通过反射执行该方法,实现不同变量的不同转义方式
                ConvertMethod methodName = field.getAnnotation(ConvertMethod.class);
                //处理注解方法名为空的情况,依然用默认方法拼接
                if ("".equals(methodName.value())) {
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                Method method = obj.getClass().getMethod(methodName.value());
                sb.append(nameLabel.value()).append(":").append(method.invoke(obj)).append(",").append(" ");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String str = sb.toString();
        //处理末尾的逗号和空格
        return !"".equals(str) ? str.substring(0, str.length() - 2) : str;
    }
}

4、执行结果

姓名:张三, 年龄:18岁, 性别:男性, 身高:175

?5、分析:

Person 类 name 属性注解中未加 sort 值,所以默认为,0 排序靠最前,age、sex、height 都是根据sort值排序值展示。

需要注意的是,这里还存在一个隐患:如果你设置的sort值两个值相等,会导致程序在转换成map进行排序的时候执行错误,抛出:java.lang.IllegalStateException: Duplicate key XXX

四、有类继承时,同样也可以实现子类加父类信息的组合转义

1、新建一个 Programmer 类 继承 Person 类

public class Programmer extends Person {

    @NameLabel(value = "秃头", sort = 5)
    @ConvertMethod("tuTouLabel")
    private Boolean tuTou;

    @NameLabel(value = "单身", sort = 6)
    @ConvertMethod("singleLabel")
    private Boolean single;

    private Boolean zhaiMan;

    public String tuTouLabel() {
        return this.tuTou ? "是" : "否";
    }

    public String singleLabel() {
        return this.single ? "是" : "否";
    }

    public Programmer(String name, String sex, Integer age, Integer height, String occupation, Boolean tuTou, Boolean single, Boolean zhaiMan) {
        super(name, sex, age, height, occupation);
        this.tuTou = tuTou;
        this.single = single;
        this.zhaiMan = zhaiMan;
    }

    public Boolean getTuTou() {
        return tuTou;
    }

    public void setTuTou(Boolean tuTou) {
        this.tuTou = tuTou;
    }

    public Boolean getSingle() {
        return single;
    }

    public void setSingle(Boolean single) {
        this.single = single;
    }
}

2、Person 类做一个小调整——修改顺序,给occupation添加@NameLabel注解,使用默认方法转义,排序为7,在Programer类两个属性排序之后

public class Person {

    @NameLabel(value = "姓名", sort = 1)
    @ConvertMethod("nameLabel")
    private String name;

    @NameLabel(value = "性别", sort = 2)
    @ConvertMethod("sexLabel")
    private String sex;

    @NameLabel(value = "年龄", sort = 3)
    @ConvertMethod("ageLabel")
    private Integer age;

    @NameLabel(value = "身高", sort = 4)
    private Integer height;

    @NameLabel(value = "职业", sort = 7)
    private String occupation;

    public Person(String name, String sex, Integer age, Integer height, String occupation) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
        this.occupation = occupation;
    }

    public String nameLabel() {
        if (name == null || name.trim().equals("")) {
            return null;
        }
        return name;
    }

    public String sexLabel() {
        if (sex == null || sex.trim().equals("")) {
            return null;
        }
        return sex + "性";
    }

    public String ageLabel() {
        if (age == null) {
            return null;
        }
        return age + "岁";
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getHeight() {
        return height;
    }

    public void setHeight(Integer height) {
        this.height = height;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }
}

3、修改转义工具类,获取父类的所有反射属性

public class FieldAndAnnotionUtil {
    private static final String EMPTY = "";

    public static String getLabelAndValue(Object obj) {
        if (Objects.isNull(obj)) {
            return EMPTY;
        }
        //获取反射
        Field[] fields = obj.getClass().getDeclaredFields();
        List<Field> fieldList = new ArrayList<>(Arrays.asList(fields));
        //获取父类的所有反射属性
        Field[] fatherFields = obj.getClass().getSuperclass().getDeclaredFields();
        Collections.addAll(fieldList, fatherFields);
        if (0 == fieldList.size()) {
            return EMPTY;
        }

        fieldList = fieldList.stream().
                //获取反射类中添加过@NameLabel注解的属性
                        filter(field -> field.isAnnotationPresent(NameLabel.class)).
                //根据@NameLabel注解的sort值进行分组
                        collect(Collectors.toMap(f -> f.getAnnotation(NameLabel.class).sort(), f -> f))
                //去重转化后根据sort排序
                .entrySet().stream().
                        sorted(Map.Entry.comparingByKey()).
                //转化为List
                        map(Map.Entry::getValue).
                        collect(Collectors.toList());

        StringBuilder sb = new StringBuilder();
        try {
            for (Field field : fieldList) {
                //授权
                field.setAccessible(true);
                Object value = field.get(obj);
                //获取到的属性为null跳出
                if (null == value) {
                    continue;
                }
                //属性没有添加NameLabel注解跳出
                if (!field.isAnnotationPresent(NameLabel.class)) {
                    continue;
                }
                NameLabel nameLabel = field.getAnnotation(NameLabel.class);
                //属性没有添加ConvertMethod注解跳出,用默认拼接方法
                if (!field.isAnnotationPresent(ConvertMethod.class)) {
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                //获取注解中的方法名,通过反射执行该方法,实现不同变量的不同转义方式
                ConvertMethod methodName = field.getAnnotation(ConvertMethod.class);
                //处理注解方法名为空的情况,依然用默认方法拼接
                if ("".equals(methodName.value())) {
                    sb.append(nameLabel.value()).append(":").append(value).append(",").append(" ");
                    continue;
                }
                Method method = obj.getClass().getMethod(methodName.value());
                sb.append(nameLabel.value()).append(":").append(method.invoke(obj)).append(",").append(" ");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String str = sb.toString();
        //处理末尾的逗号和空格
        return !"".equals(str) ? str.substring(0, str.length() - 2) : str;
    }
}

4、测试类

public class FieldAndAnnotionTest {

    public static void main(String[] args) {
//        Person person = new Person("张三", "男", 18, 175, "无");
//        String personDesc = FieldAndAnnotionUtil.getLabelAndValue(person);
//        System.out.println(personDesc);
        Programmer Programmer = new Programmer("张三", "男", 18, 175, "程序员", true, true, true);
        String personDesc = FieldAndAnnotionUtil.getLabelAndValue(Programmer);
        System.out.println(personDesc);
    }
}

5、执行结果

姓名:张三, 性别:男性, 年龄:18岁, 身高:175, 秃头:是, 单身:是, 职业:程序员

6、分析:

获取到父类的属性和值进行转义,且转义顺序按照sort值进行排序,一切正常。

五、结语

使用注解+反射来提高代码的适用性,降低代码的重复率,大体思路是这个样子,具体实现还是要视集体情况而定,盲目照抄永远不是解决问题的最好方式,且以上代码还存在两个容易发生系统错误的漏洞,还是可以继续优化的。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-13 21:36:26  更:2022-03-13 21:38:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:43:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码