使用注解+反射来降低代码重复率

一、使用场景:

实体类要转化为一串特定的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值进行排序,一切正常。

五、结语

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您解答关于Java自定义字典类、自定义注解反射注入字典值到实体类的问题。 首先,自定义字典类是一种将一些常量或者枚举类型的值封装在一起的方式。可以让代码更加易读、易维护。比如,我们可以定义一个字典类,用来存储一些常量值,如: ``` public class GenderDict { public static final Integer MALE = 1; public static final Integer FEMALE = 2; } ``` 接下来,我们可以使用自定义注解来标记需要注入字典值的实体类的属性上。比如,我们可以定义一个注解类,如: ``` @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Dictionary { String value(); } ``` 在该注解类中,我们使用@Retention和@Target注解来指定注解的保留策略和作用范围。同时,我们还可以使用value属性来指定注解的值,即该属性需要注入的字典值的名称。 最后,我们可以使用反射机制,在运行时动态地将字典值注入到实体类中。具体的实现方式如下: ``` public class DictionaryInjector { public static void injectDictionary(Object obj) throws IllegalAccessException { Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Dictionary.class)) { Dictionary dict = field.getAnnotation(Dictionary.class); String dictName = dict.value(); String dictValue = getDictValue(dictName); field.setAccessible(true); field.set(obj, dictValue); } } } private static String getDictValue(String dictName) { // 从字典类中获取对应的字典值 // 这里可以使用反射或者其他方式来实现 return GenderDict.MALE.equals(dictName) ? "男" : "女"; } } ``` 在上述代码中,我们首先使用Class对象获取实体类的所有属性,然后通过判断该属性是否被@Dictionary注解标记来确定是否需要注入字典值。如果需要注入,则从注解中获取字典值的名称,然后通过反射机制将字典值注入到实体类的属性中。 最后,我们可以在代码使用如下方式来注入字典值: ``` public class User { @Dictionary("gender") private String gender; // getters and setters } public class Main { public static void main(String[] args) throws IllegalAccessException { User user = new User(); DictionaryInjector.injectDictionary(user); System.out.println(user.getGender()); // 输出 "男" } } ``` 在上述代码中,我们首先定义了一个User类,并在其中使用@Dictionary注解标记了gender属性。然后,在Main类中,我们创建了一个User对象,并调用DictionaryInjector类的injectDictionary方法来注入字典值。最后,我们通过调用User对象的getGender方法来获取注入后的字典值。 希望这能够帮助您解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值