- 比较两个对象的字段属性值
- 对比工具类
- 数据载体对象
- 注解
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 compareFields = compareFields(student1, student2, new ArrayList<>(), null);
System.out.println(JSON.toJSONString(compareFields));
String formatStr = format(compareFields);
System.out.println("变更字段:" + formatStr);
}
public static List compareFields(Object obj1, Object obj2, List container, List 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;
}
public static String format(List 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();
}
}
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);
}
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;
}
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;
}
private static String getSwaggerNote(Class> clazz, String fieldName) {
return (String) getAnnotationValue(clazz, fieldName, ApiModelProperty.class);
}
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 {
private String filedName;
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();
}



