1.首先创建两个注解来判断字段父子级,因为字段可能会有实体类或者集合
@ChangeParent:父级注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ChangeParent {
}
@Change:子级注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Change {
}
2.变更工具类
import cn.hutool.core.collection.CollectionUtil;
import org.springframework.util.CollectionUtils;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.*;
public class ChangeUtil {
/**
* 比较两个相同对象字段值,返回一个差异对象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) {
System.out.println("索引越界:" + " 【对象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)
.build();
container.add(comparisonEntity);
}
}
return container;
}
/**
* 是否不为基本数据类型(包含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 clazz 类名
* @return 字段集合
*/
public static List<String> checkNoIgnore(Class<?> clazz) {
// 获取该类所有的公共(public)的字段。
Field[] fields = clazz.getDeclaredFields();
// 获取父类
Class<?> clazzParent = clazz.getSuperclass();
// 获取父类所有的公共(public)的字段。
Field[] fieldsParent = clazzParent.getDeclaredFields();
// 数组转换List
List<Field> list = new ArrayList<>(Arrays.asList(fields));
// 将父子类所有字段放置一个集合中
list.addAll(Arrays.asList(fieldsParent));
// 比较区别并返回变更记录
return checkChangeParent(list);
}
/**
* 比较字段集合的变更区别,产生变更记录
* @param fields 字段
* @return 变更记录
*/
public static List<String> checkChangeParent(List<Field> fields) {
// 创建接收变更字段的集合
List<String> changeString = new ArrayList<>();
// 遍历字段
for (Field field : fields) {
// 获取每个字段的@ChangeParent注解
ChangeParent changeParent = field.getAnnotation(ChangeParent.class);
if (changeParent != null) {
// 如果该字段有该注解,则添加到changeString中
changeString.add(field.getName());
// 获取该字段的类型
Class<?> clazz = field.getType();
//若为List对象,获取List的数据类型
if ("List".equals(clazz.getSimpleName())) {
// getGenericType()方法会获取到泛型中的类型 例如获取 "List<String>" 中的 "String"
Type type = field.getGenericType();
// 如果该类型属于泛型
if (type instanceof ParameterizedType) {
// 获取该泛型的类型
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] typeArgs = parameterizedType.getActualTypeArguments();
Class<?> aClass = (Class<?>) typeArgs[0];
// 获取泛型中的所有字段,并判断该字段是否为最终字段
checkChangeOrChangeParent(changeString, aClass);
}
} else {
// 若不为集合则直接获取泛型中的所有字段,并判断该字段是否为最终字段
checkChangeOrChangeParent(changeString, clazz);
}
}
}
return changeString;
}
/**
* 获取泛型中的所有字段,并判断该字段是否为最终字段
* @param changeString 字段集合
* @param clazz 类型
*/
private static void checkChangeOrChangeParent(List<String> changeString, Class<?> clazz) {
Field[] fields1 = clazz.getDeclaredFields();
for (Field field1 : fields1) {
Change change = field1.getAnnotation(Change.class);
ChangeParent changeParent1 = field1.getAnnotation(ChangeParent.class);
if (change != null) {
changeString.add(field1.getName());
} else if (changeParent1 != null) {
checkChangeParent(Arrays.asList(field1.getType().getDeclaredFields()));
}
}
}
}
注意:工具类中需引入两个注解的包!