java对比两个对象的字段值并记录输出(支持嵌套对象)

java对比两个对象的字段值(支持嵌套对象)

比较两个对象的字段属性值

equals的场景可以比较两个对象,但是不能知道具体是那个字段不一致。基于这种场景有了今天要说的这个需求。

我这里是需求是去比较新旧数据的差异,所有会去获取注解值,按照自己的需要去嵌入就可以了。

我这里需要获取获取字段的Swagger注解值去存起来,没有需求的直接把相应的代码删除就好。

参考了之前一个大佬写的,因为之前的不支持嵌套对象,就扩展了一下,去递归调用。
原文地址

对比工具类

package com.maple.springbootenv.util;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.maple.springbootenv.entity.Student;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;


public class ObjectCompareUtil {

    public static void main(String[] args) {
        Student student1 = new Student();
        Student student2 = new Student();

        List<DataComparisonEntity> compareFields = compareFields(student1, student2, new ArrayList<>(), null);
        System.out.println(JSON.toJSONString(compareFields));

        String formatStr = format(compareFields);

        System.out.println("变更字段:" + formatStr);
    }

    /**
     * 比较两个相同对象字段值,返回一个差异对象list
     *
     * @param obj1       进行属性比较的对象1
     * @param obj2       进行属性比较的对象2
     * @param ignoreList 选择忽略比较的属性数组
     * @return {@link List<DataComparisonEntity>}
     */
    public static List<DataComparisonEntity> compareFields(Object obj1, Object obj2, List<DataComparisonEntity> container, List<String> ignoreList) {

        // 忽略字段list
        if (CollectionUtils.isEmpty(ignoreList)) {
            ignoreList = new ArrayList<>();
        }

        // 必须为同一对象
        if (obj1.getClass() != obj2.getClass()) {
            throw new RuntimeException("参数1与参数2必须为同一类型对象");
        } else if (obj1 instanceof List && obj2 instanceof List && CollectionUtil.isNotEmpty((List<?>) obj1)) {
            // 字段为List
            for (int i = 0; i < ((List<?>) obj1).size(); i++) {
                try {
                    compareFields(((List<?>) obj1).get(i), ((List<?>) obj2).get(i), container, ignoreList);
                } catch (IndexOutOfBoundsException e) {
                    logInfo("索引越界:" + " 【对象1】:" + obj1 + "; 【对象2】:" + obj2 + "; 【expMessage】:" + e);
                }
            }
            return container;
        }


        // 获取所有属性描述
        Class<?> clazz = obj1.getClass();
        PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[0];
        try {
            propertyDescriptors = Introspector.getBeanInfo(clazz, Object.class).getPropertyDescriptors();
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        // 遍历属性描述符
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

            // 当前属性在忽略字段中,忽略比较当前字段
            String fieldName = propertyDescriptor.getName();
            if (ignoreList.contains(fieldName)) {
                continue;
            }

            // 当前字段的get方法
            Method readMethod = propertyDescriptor.getReadMethod();
            // 调用get方法获取obj1当前字段值
            Object fieldValue1 = null;
            // 调用get方法获取obj2当前字段值
            Object fieldValue2 = null;
            try {
                fieldValue1 = readMethod.invoke(obj1);
                fieldValue2 = readMethod.invoke(obj2);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }

            // 是否为基本数据类型(包含string)【不是基本数据类型继续比较】
            if (notBaseType(fieldValue1) && notBaseType(fieldValue2)) {
                compareFields(fieldValue1, fieldValue2, container, ignoreList);
                continue;
            }

            // 字段值不相同
            if (!Objects.equals(fieldValue1, fieldValue2)) {
                DataComparisonEntity comparisonEntity = DataComparisonEntity.builder()
                        .linkObject(clazz)
                        .filedName(fieldName)
                        .oldValue(fieldValue1)
                        .newValue(fieldValue2)
                        .swaggerNote(getSwaggerNote(clazz, fieldName))
                        .pageFieldName(getPageNote(clazz, fieldName))
                        .build();

                container.add(comparisonEntity);
            }
        }
        return container;
    }


    /**
     * 格式化差异差异对象
     *
     * @param dataComparisonEntityList List<DataComparisonEntity>
     * @return String
     */
    public static String format(List<DataComparisonEntity> dataComparisonEntityList) {

        final String gap = ":";

        final String end = "; ";

        final String changeChar = " 变更为-> ";

        if (CollectionUtil.isEmpty(dataComparisonEntityList)) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        for (DataComparisonEntity dataComparisonEntity : dataComparisonEntityList) {
            String changeDescName = gainFieldDesc(dataComparisonEntity);
            sb.append("[");
            sb.append(changeDescName);
            sb.append("]");
            sb.append(gap);
            sb.append(dataComparisonEntity.getOldValue());
            sb.append(changeChar);
            sb.append(dataComparisonEntity.getNewValue());
            sb.append(end);
        }

        sb.setLength(sb.length() - 2);
        return new String(sb);
    }

    private static String gainFieldDesc(DataComparisonEntity entity) {
        String swaggerNote = entity.getSwaggerNote();
        String pageFieldName = entity.getPageFieldName();
        // 有页面注解时直接取页面注解
        if (StringUtils.isNotEmpty(pageFieldName)) {
            return pageFieldName;
        } else if (StringUtils.isNotEmpty(swaggerNote)) {
            return swaggerNote;
        } else {
            return entity.getFiledName();
        }
    }


    /**
     * 是否不为基本数据类型(包含String);不是基本数据类型返回true
     *
     * @param obj object
     * @return boolean
     */
    private static boolean notBaseType(Object obj) {
        if (Objects.isNull(obj)) {
            return false;
        }
        return !(obj instanceof Byte)
                && !(obj instanceof Short)
                && !(obj instanceof Integer)
                && !(obj instanceof Long)
                && !(obj instanceof Float)
                && !(obj instanceof Double)
                && !(obj instanceof Character)
                && !(obj instanceof Boolean)
                && !(obj instanceof String)
                && !(obj instanceof Date);
    }

    /**
     * 处理首字母大写错误的字段名
     *
     * @param fieldName 字段名
     * @return 字段名
     */
    private static String handlerFieldName(String fieldName) {
        if (StringUtils.isEmpty(fieldName)) {
            return fieldName;
        }

        final char charA = 65;
        final char charZ = 90;
        char firstChar = fieldName.charAt(0);
        char secondChar = fieldName.charAt(1);
        // 第一个字符和第二个字符都是大写,存在问题
        if (firstChar >= charA && firstChar <= charZ && secondChar >= charA && secondChar <= charZ) {
            return fieldName.replace(fieldName.charAt(0), (char) (fieldName.charAt(0) + 32));
        }
        return fieldName;
    }


    /**
     * 获取注解的value值
     *
     * @param clazz           添加注解的字段
     * @param fieldName       字段名
     * @param annotationClass 注解类型
     * @return
     */
    private static Object getAnnotationValue(Class<?> clazz, String fieldName, Class<? extends Annotation> annotationClass) {
        // 处理问题字段
        fieldName = handlerFieldName(fieldName);

        try {
            Field field = clazz.getDeclaredField(fieldName);

            if (field.isAnnotationPresent(annotationClass)) {
                Annotation annotation = field.getAnnotation(annotationClass);
                try {
                    return annotation.getClass().getDeclaredMethod("value").invoke(annotation);
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    logInfo("当前注解没有value属性:" + " 【注解类型】:" + annotationClass + "; 【字段名】:" + fieldName + "; 【expMessage】:" + e);
                    return null;
                }
            }
        } catch (NoSuchFieldException e) {
            // 获取父类字段
            Class<?> superClass = clazz.getSuperclass();
            if (superClass == Object.class) {
                logInfo("获取注解value失败:" + "; 【字段名】:" + fieldName + "; 【expMessage】:" + e);
                return null;
            }
            try {
                return getAnnotationValue(superClass, fieldName, annotationClass);
            } catch (Exception exception) {
                logInfo("获取注解value失败:" + " 【对象类型】:" + clazz + "; 【字段名】:" + fieldName + "; 【expMessage】:" + e);
                return null;
            }
        }

        return null;
    }


    /**
     * 获取字段的swagger注解值
     *
     * @param clazz     字段类型
     * @param fieldName 字段名
     * @return ApiModelProperty(" value ")
     */
    private static String getSwaggerNote(Class<?> clazz, String fieldName) {
        return (String) getAnnotationValue(clazz, fieldName, ApiModelProperty.class);
    }

    /**
     * 获取页面注解
     *
     * @param clazz     字段类型
     * @param fieldName 字段名
     * @return PageNote(" value ")
     */
    private static String getPageNote(Class<?> clazz, String fieldName) {
        return (String) getAnnotationValue(clazz, fieldName, PageNote.class);
    }

    private static void logInfo(String info) {
        System.out.println(info);
    }
}

数据载体对象

package com.maple.springbootenv.util;

import lombok.Builder;
import lombok.Data;


@Data
@Builder
public class DataComparisonEntity {

    /**
     * DTO字段名
     */
    private String filedName;

    /**
     * swagger注解
     */
    private String swaggerNote;

    /**
     * 页面字段名
     */
    private String pageFieldName;

    /**
     * 旧数据
     */
    private Object oldValue;

    /**
     * 新数据
     */
    private Object newValue;

    /**
     * 关联对象
     */
    private Class<?> linkObject;
}

注解

package com.maple.springbootenv.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PageNote {

    String value();
}

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值