用反射与注解获取两个不同对象间的属性值差异

业务场景:操作人将一条记录的某些字段更改了,想要记录操作日志,谁将某个字段改为了什么

分析:记录操作日志,也就是拿前端传来的数据与数据库中数据作比较,就是比较 dto 与 entity 间属性值的差异,两个对象属性不完全一致

解决思路:自定义一个注解加在 dto 需要比较的属性上,在注解中定义该属性的中文名称,再利用反射遍历 dto 中所有加了注解的属性,拿属性名去 entity 中找同名属性,再比较属性值;如果属性类型是 Integer 等整形类型,结果输出”由 1 变为 2“ ,这种语义不好理解,可在注解上为该属性配置枚举,通过反射拿到数字对应的描述

自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface CompareFiled {
    String filedChineseName(); // 属性的中文名
    Class<?> enumClass() default Enum.class; // 属性对应枚举类类型
}

核心类:

public class CompareTwoObject {
	// 通过传入的code,通过反射遍历所有枚举值,拿到对应desc
    private static String getEnumValue(Class<?> clazz, Object val) {
        try {
            Method getCode = clazz.getDeclaredMethod("getCode");
            Method getDesc = clazz.getDeclaredMethod("getDesc");

            Object[] enumConstants = clazz.getEnumConstants();
            for (Object enumConstant : enumConstants) {
                if (getCode.invoke(enumConstant).equals(val)) {
                    return getDesc.invoke(enumConstant).toString();
                }
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return "未找到对应枚举值";
    }

    /**
     * 比较两个对象属性值的不同,返回一串字符串,格式为:【{属性名} 由 {旧值} 变为 {新值}】
     * 程序遍历newVal对象中加了@CompareFiled注解的字段,如果字段为null或字段在oldVal中不存在则不比较
     * 如果该字段上有对应的枚举类,会将该字段值与枚举的code相匹配,返回对应name
     */
    public static String compare(Object newVal, Object oldVal) {
        StringBuilder result = new StringBuilder();
        Class<?> newClass = newVal.getClass();
        Class<?> oldClass = oldVal.getClass();

        Field[] oldValFields = oldClass.getDeclaredFields();
        Set<String> oldValFiledSet = new HashSet<>(oldValFields.length);

        for (Field oldValField : oldValFields) { // 将oldVal的所有属性放入set集合中,方便查找
            oldValField.setAccessible(true);
            oldValFiledSet.add(oldValField.getName());
        }

        try {
            Field[] newValFileds = newClass.getDeclaredFields();
            for (Field newValFiled : newValFileds) {
                newValFiled.setAccessible(true);

                if (newValFiled.isAnnotationPresent(CompareFiled.class)) {
                  	// 如果属性值为null,或oldVal中不包含该属性,不比较
                    if (newValFiled.get(newVal) != null && oldValFiledSet.contains(newValFiled.getName())) {
                        Field oldValFiled = oldClass.getDeclaredField(newValFiled.getName());
                        oldValFiled.setAccessible(true);
                      
						// 比较两个属性值是否相等
                        if (!newValFiled.get(newVal).equals(oldValFiled.get(oldVal))) {
                            CompareFiled cf = newValFiled.getAnnotation(CompareFiled.class);
                            result.append("【").append(cf.filedChineseName());
							// 如果注解上配置了枚举,则获取枚举中值
                            if (cf.enumClass().getSuperclass() == Enum.class) {
                                result.append(" 由 ").append(getEnumValue(cf.enumClass(), oldValFiled.get(oldVal)))
                                        .append(" 变为 ").append(getEnumValue(cf.enumClass(), newValFiled.get(newVal))).append("】");
                            } else {
                                result.append(" 由 ").append(oldValFiled.get(oldVal))
                                        .append(" 变为 ").append(newValFiled.get(newVal)).append("】");
                            }
                        }
                    }
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return result.toString();
    }

}

测试Demo

public class Demo {

    @Getter
    enum NumEnum {
        ONE(1, "数字一"),
        TWO(2, "数字二");

        private Integer code;
        private String desc;

        NumEnum(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }
    }

    @Data
    class Entity1 {
        @CompareFiled(filedChineseName = "数字", enumClass = NumEnum.class)
        private Integer num = 1;
        @CompareFiled(filedChineseName = "名字")
        private String name = "XXX";
        @CompareFiled(filedChineseName = "时间")
        private String time = "20:10";
        @CompareFiled(filedChineseName = "日期")
        private String date = "2021";
    }

    @Data
    class Entity2 {
        private Integer num = 2;
        private String name = "XXX";
        private String updateTime = "20:20";
        private String date = "2020";
    }

    @Test
    public void test() {
        System.out.println(CompareTwoObject.compare(new Entity1(), new Entity2()));
      	// 结果打印 【数字 由 数字二 变为 数字一】【日期 由 2020 变为 2021】
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值