对比两个Java对象属性值的差异代码示例

需求

相同类型的对象A和对象B,要知道他们两个之间属性值的差异,另外一种场景是将一个对象更新时,想知道这个对象的哪些属性发生了变更。

思路

通过反射获取对象A和对象B的所有属性的值,然后再进行对比,将值不相同的信息记录并返回。
对比结果中为了方便人去阅读,以及标识下哪些属性才需要做对比,所以设计一个注解,来表明属性的名称以及作为对比的对象。
由于反射对于虚拟机来说性能损耗较大,所以将反射信息缓存下来,提升效率。

代码示例

@DiffProperty注解

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

/**
 * 对比项属性注解
 *
 * 只有标记了此注解的属性才会进行对比
 */
@Target({
    
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DiffProperty {
    
    
    /**
     * 对比项名称
     * <p>
     * 如果不指定,则取字段的名称
     *
     * @return
     */
    String name() default "";
}

DiffResult 对比结果

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * 对比结果
 */
@Data
@AllArgsConstructor
public class DiffResult {
    
    
    /**
     * 字段名称
     */
    private String fieldName;
    /**
     * 对比项名称
     */
    private String propertyName;

    /**
     * A对象的值
     */
    private Object valueA;

    /**
     * B对象的值
     */
    private Object valueB;
}

ObjectUtil 对象属性值对比工具类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.*;

public class ObjectUtil {
    
    

    private static final Map<Class<?>, Map<String, Field>> DIFF_PROPERTY_CACHE = new ConcurrentReferenceHashMap<>();

    /**
     * 比较两个相同类型的对象属性之间的差异
     * @param a 对象A
     * @param b 对象B
     * @param <T> 对象类型
     * @return 差异列表
     * @throws IllegalAccessException
     */
    @SneakyThrows
    public static <T> List<DiffResult> diff(T a, T b) {
    
    
        if (equals(a, b)) {
    
    
            return Collections.EMPTY_LIST;
        }

        // 获取并缓存一个类中需要对比的字段
        Map<String, Field> cachedDiffPropertyMap = DIFF_PROPERTY_CACHE.computeIfAbsent((a != null ? a : b).getClass(),
                clazz -> {
    
    
                    Map<String, Field> diffPropertyMap = new HashMap<>();
                    for (Field field : clazz.getDeclaredFields()) {
    
    
                        DiffProperty diffProperty = field.getAnnotation(DiffProperty.class);
                        if (diffProperty != null) {
    
    
                            field.setAccessible(true);
                            String name = diffProperty.name();
                            if (name.isEmpty()) {
    
    
                                name = field.getName();
                            }
                            diffPropertyMap.put(name, field);
                        }
                    }
                    return diffPropertyMap;
                });

        // 进行对比,并将对比结果返回
        List<DiffResult> diffResultList = new ArrayList<>();
        for (Map.Entry<String, Field> fieldEntry : cachedDiffPropertyMap.entrySet()) {
    
    
            Field field = fieldEntry.getValue();
            Object aValue = a == null ? null : field.get(a);
            Object bValue = b == null ? null : field.get(b);
            if (!equals(aValue, bValue)) {
    
    
                diffResultList.add(new DiffResult(field.getName(), fieldEntry.getKey(), aValue, bValue));
            }
        }
        return diffResultList;
    }

    /**
     * 比较两个对象是否相同
     * @param a 对象A
     * @param b 对比B
     * @return 相同返回true,不同返回false
     */
    public static boolean equals(Object a, Object b) {
    
    
        if (a == null) {
    
    
            return b == null;
        }

        if (a == b) {
    
    
            return true;
        }

        return a.equals(b);
    }
}

测试

public class Test01 {
    
    
    @Data
    @AllArgsConstructor
    public static class Dto1{
    
    
        private Long id;

        @DiffProperty(name = "编码")
        private String code;

        @DiffProperty(name = "名称")
        private String name;
    }

    public static void main(String[] args) throws IllegalAccessException {
    
    
        Dto1 dto1 = new Dto1(100L,"zsan","张三");
        Dto1 dto2 = new Dto1(200L,"zhsan","张三");

        List<DiffResult> diffResultList = ObjectUtil.diff(dto1, dto2);
        for (DiffResult diffResult : diffResultList) {
    
    
            System.out.println(String.format("%s,A:%s,B:%s", diffResult.getPropertyName(),
                    diffResult.getValueA(), diffResult.getValueB()));
        }
    }
}

执行结果

编码,A:zsan,B:zhsan

猜你喜欢

转载自blog.csdn.net/qq_39609993/article/details/129481572