需求
相同类型的对象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