JAVA通过反射对比2个同类型对象属性值的差异,并以集合类型返回

工作中接了一个这样的需求:进行修改操作时,记录一个修改日志表。需要记录改动过的属性,原来的值,现在的值等。此功能有一个亮点,接收的参数为diff(T a, T b)都为泛型,以泛型的方式做处理之前的开发中确实用的比较少。

解决方式如下:

public class DiffUtils {

    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集合。
        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()) {
                        field.setAccessible(true);
                        String 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();
            // field.get(a) 获取a对象field这个属性名的属性值!
            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);
    }

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

    /**
     * 老对象的值
     */
    private Object oldValue;

    /**
     * 新对象的值
     */
    private Object newValue;
}

测试代码:

public static void test() {
        SysUser sysUser = new SysUser(1L);
        sysUser.setNickName("张三");
        sysUser.setRoleId(333L);
        sysUser.setSex("男");
        SysUser sysUser1 = new SysUser(2L);
        sysUser1.setNickName("李四");
        sysUser1.setRoleId(666L);
        sysUser1.setSex("女");
        List<DiffResult> diff = ChangeLogUtils.diff(sysUser, sysUser1);
        for (DiffResult diffResult : diff) {
            System.out.println(diffResult);
        }
}

控制台输出结果:

拓展:上述为 对比SysUser所有属性,如果想要控制部分属性对比,部分不对比可以增加注解。

@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 "";
}

修改获取对比字段时的代码,增加注解判断。然后在需要对比的实体类属性上增加@DiffProperty。即可实现部分字段对比。

        // 获取并缓存一个类中需要对比的字段
        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;
                });

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值