基于Java反射机制来比较两个实体对象的属性是否发生变化

业务场景

某高校教职工职称评审项目,有一个“一表通”信息维护模块,教职工可对自己的各方面信息(比如基本情况、论文著作等)进行纠错,然后每次修改发生变化的内容需要对应的审核部门进行审核通过以后才能真正修改库数据。教职工的信息项多则40~50项,总不能写一堆的if-else对每个字段进行判断,然后再筛选出发生变化的字段,这样太累了!更何况信息项有加有减的时候我还得去维护相应的代码。

最后想到用Java的反射机制并结合自定义属性注解,拿到新旧对象所有的属性集合,包括属性名称(name)、属性中文注释(姓名)以及属性值(张三),然后拿着两个属性集合再进行比对,拿到本次修改发生变化的属性信息。

代码实现

1、创建一个实体类对象 StaffBaseInfo【教职工基本信息】

@Data
@TableName("staff_base_info")
public class StaffBaseInfoDO implements Serializable {

    @TableId
    private String id;
    /**
     * 单位号
     */
    @PropertyName(name = "单位号")
    private String deptNumber;
    /**
     * 工号
     */
    @PropertyName(name = "工号")
    private String workNumber;
    /**
     * 姓名
     */
    @PropertyName(name = "姓名")
    private String name;
    /**
     * 性别
     */
    @PropertyName(name = "性别", isDict = true)
    private String gender;
    /**
     * 出生日期
     */
    @PropertyName(name = "出生日期")
    private String birthday;
    /**
     * 身份证件类型
     */
    @PropertyName(name = "身份证件类型", isDict = true)
    private String idType;
    /**
     * 身份证件号码
     */
    @PropertyName(name = "身份证件号码")
    private String idNumber;
    /**
     * 民族
     */
    @PropertyName(name = "民族", isDict = true)
    private String nation;
    /**
     * 政治面貌
     */
    @PropertyName(name = "政治面貌", isDict = true)
    private String politicsStatus;
    /**
     * 参加工作时间
     */
    @PropertyName(name = "参加工作时间")
    private String workTime;
    /**
     * 到校(院)工作时间
     */
    @PropertyName(name = "到校(院)工作时间")
    private String schoolWorkTime;
    /**
     * 最高学历
     */
    @PropertyName(name = "最高学历", isDict = true)
    private String highestEducation;
    /**
     * 最高学位
     */
    @PropertyName(name = "最高学位", isDict = true)
    private String highestDegree;

	........省略N多属性.........
	
}

其中,自定义了一个注解@PropertyName,它有两个属性,name代表的是属性的中文注释,isDict代表该属性是否为字典项,默认为false;如果设置为true,则代表该属性存储的是字典代码,需要将字典项代码翻译成对应的字典项名称,具体如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PropertyName {
    // 字段属性中文注释
    String name() default "";
    // 字段属性是否为字典项代码
    boolean isDict() default false;
}

2、创建 PropertyModelInfo 类,利用反射机制拿到的字段属性信息

@Data
public class PropertyModelInfo {

    /**
     * 属性名称
     */
    private String propertyName;
    /**
     * 属性注释
     */
    private String propertyComment;
    /**
     * 属性值
     */
    private Object value;
    /**
     * 返回值类型
     */
    private Class<?> returnType;

}

3、创建 ModifiedPropertyInfo 类,用于记录修改后发生变化的字段属性信息

@Data
public class ModifiedPropertyInfo implements Serializable {

    /**
     * 发生变化的属性名称
     */
    private String propertyName;
    /**
     * 发生变化的属性注释
     */
    private String propertyComment;
    /**
     * 修改前的值
     */
    private Object oldValue;
    /**
     * 修改后的值
     */
    private Object newValue;

}

4、创建工具类 CompareObjectPropertyUtil,用了比较两个实体对象的属性是否发生变化

public class CompareObjectPropertyUtil {

    private static final Logger log = LoggerFactory.getLogger(CompareObjectPropertyUtil.class);

    /**
     * 比较两个对象不同的属性并记录返回
     *
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @param ignoreProperties 可忽略对比的属性
     * @return java.util.List<com.sdyy.staff.utils.ModifiedPropertyInfo>
     */
    public static <T> List<ModifiedPropertyInfo> getDifferentProperty(T oldObj , T newObj , String... ignoreProperties){
        if (oldObj != null && newObj != null) {
            // 比较全部字段
            if (ignoreProperties == null || ignoreProperties.length > 0) {
                if (oldObj.equals(newObj)) {
                    return Collections.emptyList();
                }
            }
            List<PropertyModelInfo> oldObjectPropertyValue = getObjectPropertyValue(oldObj, ignoreProperties);
            if (!CollectionUtils.isEmpty(oldObjectPropertyValue)) {
                List<ModifiedPropertyInfo> modifiedPropertyInfos = new ArrayList<>(oldObjectPropertyValue.size());

                List<PropertyModelInfo> newObjectPropertyValue = getObjectPropertyValue(newObj, ignoreProperties);
                Map<String , Object> objectMap = new HashMap<>(newObjectPropertyValue.size());
                // 获取新对象所有属性值
                for (PropertyModelInfo propertyModelInfo : newObjectPropertyValue) {
                    String propertyName = propertyModelInfo.getPropertyName();
                    Object value = propertyModelInfo.getValue();
                    objectMap.put(propertyName , value);
                }

                for (PropertyModelInfo propertyModelInfo : oldObjectPropertyValue) {
                    String propertyName = propertyModelInfo.getPropertyName();
                    String propertyComment = propertyModelInfo.getPropertyComment();
                    Object value = propertyModelInfo.getValue();
                    if (objectMap.containsKey(propertyName)) {
                        Object newValue = objectMap.get(propertyName);
                        ModifiedPropertyInfo modifiedPropertyInfo = new ModifiedPropertyInfo();
                        if (value != null && newValue != null) {
                            if (!value.equals(newValue)) {
                                modifiedPropertyInfo.setPropertyName(propertyName);
                                modifiedPropertyInfo.setPropertyComment(propertyComment);
                                modifiedPropertyInfo.setOldValue(value);
                                modifiedPropertyInfo.setNewValue(newValue);
                                modifiedPropertyInfos.add(modifiedPropertyInfo);
                            }
                        } else if((newValue == null && value != null && !StringUtils.isBlank(value.toString()))
                                || (value== null && newValue != null && !StringUtils.isBlank(newValue.toString()))) {
                            modifiedPropertyInfo.setPropertyName(propertyName);
                            modifiedPropertyInfo.setPropertyComment(propertyComment);
                            modifiedPropertyInfo.setOldValue(value);
                            modifiedPropertyInfo.setNewValue(newValue);
                            modifiedPropertyInfos.add(modifiedPropertyInfo);
                        }
                    }
                }
                return modifiedPropertyInfos;
            }
        }
        return Collections.emptyList();
    }


    /**
     * 通过反射获取对象的属性名称、getter返回值类型、属性值等信息
     *
     * @param obj 对象
     * @param ignoreProperties 可忽略比对的属性
     * @return java.util.List<com.sdyy.staff.utils.PropertyModelInfo>
     */
    public static <T> List<PropertyModelInfo> getObjectPropertyValue(T obj , String... ignoreProperties){
        if (obj != null) {
            Class<?> objClass = obj.getClass();
            PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(objClass);
            List<PropertyModelInfo> modelInfos = new ArrayList<>(propertyDescriptors.length);

            Field[] fields = objClass.getDeclaredFields();
            List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
            for(Field field : fields) {
                field.setAccessible(true);
                String fieldName = field.getName();
                if(ignoreList == null || !ignoreList.contains(fieldName)) {
                    PropertyModelInfo propertyModelInfo = new PropertyModelInfo();
                    propertyModelInfo.setPropertyName(fieldName);
                    propertyModelInfo.setReturnType(field.getType());
                    Object fieldValue = getFieldValueByName(fieldName, obj);
                    // 通过自定义注解拿到属性注释
                    if(field.isAnnotationPresent(PropertyName.class)) {
                        PropertyName annotation = field.getAnnotation(PropertyName.class);
                        propertyModelInfo.setPropertyComment(annotation.name());
                        // 如果是字典项,则将code转换成name
                        // 注意:获取字典项这部分代码需要结合自己的业务实现,我这里是从静态文件加载进来的
                        if(annotation.isDict()) {
                            List<DictDTO> dictList = JSONObject.parseArray(JSONObject.toJSONString(DictionaryConstant.dictionaryMap.get(fieldName)), DictDTO.class);
                            Object finalFieldValue = fieldValue;
                            if(finalFieldValue != null && !"".equals(finalFieldValue)) {
                                Optional<DictDTO> first = dictList.stream().filter(dictDTO -> dictDTO.getValue().equals(String.valueOf(finalFieldValue))).findFirst();
                                if(first.isPresent()) {
                                    fieldValue = first.get().getText();
                                }
                            }
                        }
                    }
                    propertyModelInfo.setValue(fieldValue == null ? "":fieldValue);
                    modelInfos.add(propertyModelInfo);
                }
            }
            return modelInfos;
        }
        return Collections.emptyList();
    }

    private static Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return null;
        }
    }

}

5、测试

public static void main(String[] args) {
    // 修改前数据
    StaffBaseInfoDO oldStaff = new StaffBaseInfoDO();
    oldStaff.setName("张三");
    oldStaff.setBirthday("1987-01-02");
    oldStaff.setBirthPlace("北京市");
    // 最高学位为字典项
    oldStaff.setHighestDegree("408");
    // 修改后数据
    StaffBaseInfoDO newStaff = new StaffBaseInfoDO();
    newStaff.setName("张三");
    newStaff.setBirthday("1987-01-02");
    newStaff.setBirthPlace("山东济南");
    newStaff.setHighestDegree("308");

    List<ModifiedPropertyInfo> differentProperty = CompareObjectPropertyUtil.getDifferentProperty(oldStaff, newStaff);
    System.out.println("本次修改共计发生"+differentProperty.size()+"处变化,具体如下所示:");
    differentProperty.forEach(diff -> {
        System.out.println(diff.getPropertyComment() + ":"+ "修改前为【"+diff.getOldValue() + "】,修改后变为【"+diff.getNewValue()+"】");
    });

}

打印结果如下:

本次修改共计发生2处变化,具体如下所示:
出生地:修改前为【北京市】,修改后变为【山东济南】
最高学位:修改前为【工学学士学位】,修改后变为【工学硕士学位】

结语

至此,简单的比较修改前后对象发生变化的属性工具类就完成了,如果实体类属性有新增或减少,只需要维护好实体类本身就可以,不需要改动工具类里面的逻辑。

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java中,你可以使用反射机制来动态获取实体对象属性反射允许你在运行时检查和操作类、接口、字段和方法等信息。 下面是一个示例代码,展示了如何使用反射动态获取实体对象属性: ```java import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { // 创建实体对象 Person person = new Person("Alice", 25); // 获取实体对象属性值 Class<?> clazz = person.getClass(); Field nameField = clazz.getDeclaredField("name"); Field ageField = clazz.getDeclaredField("age"); nameField.setAccessible(true); ageField.setAccessible(true); String name = (String) nameField.get(person); int age = (int) ageField.get(person); System.out.println("Name: " + name); System.out.println("Age: " + age); } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } } ``` 在这个示例中,我们创建了一个名为 Person 的实体类,它有两个私有属性:name 和 age。使用反射,我们获取了这两个属性的 Field 对象,并通过调用 Field 的 get() 方法来获取实体对象属性值。 请注意,由于属性是私有的,我们需要使用 setAccessible(true) 来设置访问权限。 通过反射,你可以动态地获取实体对象属性,并进行相应的操作。但是请注意,反射操作可能会带来一些性能上的开销,因此在实际使用中应该谨慎使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值