使用java反射和自定义注解比较编辑时新旧对象内属性值

本文介绍了在编辑场景下,如何使用Java反射和自定义注解来比较新旧对象属性的变化,包括记录字段的中英文名称、前后值,并特别处理了枚举类型的转换。传统反射方法无法获取字段的中文名称和中文显示值,而通过自定义注解,可以在实体类属性上标注中文解释和枚举信息,从而实现完整的信息记录。

一、需求:

编辑时,需要判断每一个字段是否有变化并记录。

       如果有变化,需要记录变化的字段信息:包括字段中文名称、字段的英文key、字段变更前的值、字段变更后的值。

       ps:字段有可能是枚举,即数据库存储的是英文code,前端显示的是code对应中文(一般枚举这种,接口交互使用英文code,数据库存储也是英文code)。此时变更前、后的值,需要把英文code和中文名称全部记录下来。

       即最终要记录的,是:字段的英文key、字段中文名称、字段变更前的数据库值、字段变更前的中文显示值、字段变更后的数据库值、字段变更后的中文显示值。

       

二、只使用反射的不足:

纯粹使用反射,只能拿到:字段的英文key、字段变更前的数据库值、字段变更后的数据库值。

缺失:字段中文名称、字段变更前的中文显示值、、字段变更后的中文显示值。

 

三、自定义注解+反射:

3-1、自定义注解:

在要比较的对象的属性上,添加自定义注解,注解可以标注每一个属性的对应中文名称、相应枚举情况

import java.lang.annotation.*;

/**
 * 自定义注解
 *   使用在属性上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AttributeDesc {

    //属性名称
    String notes() default "";

    //属性是否属于枚举类型,""不是,如果不为"",比如是"card-type",那说明,此是枚举类,并且对应的枚举的关键字是"card-type",只要从数据字典内,类型是"card-type"的一批数据里去匹配就好
    String enumCode() default "";
}

3-2、实体类内使用:

 字段的英文key为"stockholderCardType"的中文解释是"股东证件类型",是枚举,枚举对应的数据字典类型是"card-type",由"@AttributeDesc(notes = "股东证件类型", enumCode = "card-type")"标记出。字段数据库值 存储 对象反射得到的值,字段中文显示值存储,通过 对象反射得到的值 和 类型"card-type",去数据字典内查询到的中文值。

字段的英文key为"legalPerson"的中文解释是"法定代表人",不是枚举,由"@AttributeDesc(notes = "法定代表人")"标记出。字段数据库值 和 字段中文显示值,存储成一样的,都是 通过对象反射得到的值。

public class demo{



    @AttributeDesc(notes = "股东证件类型", enumCode = "card-type")
    private String stockholderCardType;//股东证件类型(数据字典card-type)

    @AttributeDesc(notes = "法定代表人")
    private String legalPerson;//法定代表人


    public String getStockholderCardType() {
        return stockholderCardType;
    }

    public void setStockholderCardType(String stockholderCardType) {
        this.stockholderCardType = stockholderCardType;
    }

    public String getLegalPerson() {
        return legalPerson;
    }

    public void setLegalPerson(String legalPerson) {
        this.legalPerson = legalPerson;
    }
}

 

3-3、反射的比较方法:

此处暂时只写了String类型的属性判断

/**
     * 比较两个对象内属性值,返回变更详情
     * 标记了自定义注解的属性,才进行比较
     *
     * @param oldDb   旧实体类
     * @param newDb 新实体类
     * @return 变更详情
     */
    public JSONArray compileTwoObjects(Object oldDb, Object newDb) {
        JSONArray jsonArray = new JSONArray();

        //旧实体转为类
        Class<Object> oldClass = (Class<Object>) oldDb.getClass();
        //新实体转为类
        Class<Object> newClass = (Class<Object>) newDb.getClass();
        //获取实体类内属性列表(需要比较的两个对象,应该是类相同,或者对象内字段一致,不然比较无意义)
        Field[] oldFiles = oldClass.getDeclaredFields();//获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段

        //遍历属性-比较差异
        for (Field field : oldFiles) {
            //获取属性的get方法名称
            String getMethodName = "get"
                    + field.getName().substring(0, 1).toUpperCase()
                    + field.getName().substring(1);
            try {
                //通过方法名称获取旧实体类对应方法
                Method oldMeth = (Method) oldClass.getMethod(getMethodName);
                //通过方法名称获取新实体类对应方法
                Method newMeth = (Method) newClass.getMethod(getMethodName);
                //获取属性上自定义注解
                AttributeDesc meta = field.getAnnotation(AttributeDesc.class);
                try {
                    //属性上有注解,才需要进行比较
                    if (meta != null) {
                        //注解-属性名称
                        String note = meta.notes();
                        //属性是否属于枚举类型:""不是,不是""的话没说明此字段是枚举类,数据库存储的和要显示的不一致
                        String enumCode = meta.enumCode();
                        //对象-旧属性值
                        Object oldValObj= oldMeth.invoke(oldDb);
                        //对象-新属性值
                        Object newValObj = newMeth.invoke(newDb);

                        //比较
                        if (oldValObj instanceof String) {
                            //字符串可以处理Null
                            String oldVal = (String) oldValObj;
                            String newVal = (String) newValObj;
                            if (!oldVal.equals(newVal)) {
                                JSONObject jsonObject = new JSONObject();
                                jsonObject.put("变更字段",field.getName());
                                jsonObject.put("变更字段中文",note);
                                jsonObject.put("变更前值",oldVal);
                                if (!StringUtils.isEmpty(enumCode) && !StringUtils.isEmpty(oldVal)) {
                                    SysDictionary sysDictionary = sysDictionaryService.getSysDictionary(enumCode, oldVal);
                                    if (null != sysDictionary) {
                                        jsonObject.put("变更前值名称",sysDictionary.getKeyDesc());
                                    }
                                } else {
                                    jsonObject.put("变更前值名称",oldVal);
                                }
                                jsonObject.put("变更后值",newVal);
                                if (!StringUtils.isEmpty(enumCode) && !StringUtils.isEmpty(newVal)) {
                                    SysDictionary sysDictionary = sysDictionaryService.getSysDictionary(enumCode, newVal);
                                    if (null != sysDictionary) {
                                        jsonObject.put("变更后值名称",sysDictionary.getKeyDesc());
                                    }
                                } else {
                                    jsonObject.put("变更后值名称",newVal);
                                }
                                jsonArray.add(jsonObject);
                            }
                        }
                        //TODO 其他类型
                    }

                } catch (IllegalAccessException e) {
                    log.debug("private修饰的成员(变量和方法),可以通过setAccessible(true)暴力获取");
                } catch (IllegalArgumentException e) {
                    log.debug("无法转换类型导致 java.lang.IllegalArgumentException(注意:反射获取或者修改一个变量的值时,编译器不会进行自动装/拆箱,所以int 和Integer需手动修改)");
                } catch (InvocationTargetException e) {
                    log.debug("由Method.invoke(obj, args...)方法抛出.当被调用的方法的内部抛出了异常而没有被捕获时,将由此异常接收");
                }
            } catch (NoSuchMethodException e) {
                log.debug("由getMethod(String name, Class<?>... parameterTypes)方法抛出.没有这个方法可显示调用");
            } catch (SecurityException e) {
                log.debug("安全异常");
            }
        }
        return jsonArray;
    }

四、最终结果:

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值