前言
Java反射机制:是指在运行时动态地获取、检查和操作类、对象、方法和属性的能力。使用反射,您可以在运行时获取类的信息、实例化对象、调用方法和访问属性,而无需在编译时明确地引用它们。
字段比较时,主要利用反射机制进行,将两个实例对象的所有字段进行一一比较,具体实例如下:
1、定义工具类
FieldComparisonUtil:
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* @Author:
* @Date: 2023/1/9 11:27
* @Description: 字段比较
**/
public class FieldComparisonUtil {
/**
* 直接返回一个新的对象,并且对象的值 只有被修改的部分
*
* @param old
* @param source
* @param isParent
* @param target 目标对象
* @return
*/
public static void compareFieldsToNewObject(Object old, Object source, Boolean isParent, Object target) {
Map<String, Object> map = compareFields(old, source, isParent);
if (map.size() > 0) {
try {
List<Field> fieldList = new ArrayList<>();
Class<?> clazz = target.getClass();
// 获取字段
Field[] fields = clazz.getDeclaredFields();
fieldList.addAll(Arrays.asList(fields));
// 获取父类字段
if (isParent) {
Class<?> superclass = clazz.getSuperclass();
Field[] parentFields = superclass.getDeclaredFields();
fieldList.addAll(Arrays.asList(parentFields));
}
for (Field field : fieldList) {
field.setAccessible(true);
String name = field.getName();
if (map.get(name) != null) {
field.set(target, map.get(name));
}
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
/**
* @param old 进行属性比较的原始数据
* @param source 进行属性比较的新数据
* @param isParent 是否存在父类
* @return
*/
public static Map<String, Object> compareFields(Object old, Object source, Boolean isParent) {
Map<String, Object> map = new HashMap<>(8);
try {
// 只有两个对象都是同一类型的才有可比性
if (old.getClass() == source.getClass()) {
List<Field> fieldList = new ArrayList<>();
Class<?> clazz = old.getClass();
// 获取字段
Field[] fields = clazz.getDeclaredFields();
fieldList.addAll(Arrays.asList(fields));
// 获取父类字段
if (isParent) {
Class<?> superclass = clazz.getSuperclass();
Field[] parentFields = superclass.getDeclaredFields();
fieldList.addAll(Arrays.asList(parentFields));
}
// 这里就是所有的属性了
for (Field field : fieldList) {
field.setAccessible(true);
// 属性名
String fieldName = field.getName();
// 在old上调用get方法等同于获得old的属性值
Object objBefore = field.get(old);
// 在source上调用get方法等同于获得source的属性值
Object objAfter = field.get(source);
// 比较字段同时为空时,则表示是一样的值
if (objAfter instanceof String) {
if (StringUtils.isEmpty(objBefore) && StringUtils.isEmpty(objAfter)) {
continue;
}
}
// 比较是否一致
boolean equals = Objects.equals(objBefore, objAfter);
// 不一致则进行记录
if (!equals) {
if (objAfter != null) {
/**
* 如果要使用字符串记录,那么对特殊的数据,需要进行转换
if (objAfter instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatDate = sdf.format((Date) objAfter);
map.put(fieldName, formatDate);
} else if (objAfter instanceof LocalDateTime) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDate = ((LocalDateTime) objAfter).format(formatter);
map.put(fieldName, formatDate);
} else if (objAfter instanceof LocalDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formatDate = ((LocalDate) objAfter).format(formatter);
map.put(fieldName, formatDate);
}
**/
map.put(fieldName, objAfter);
}
}
}
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return map;
}
}
2、实体类定义
TestEntity:
@Data
public class TestEntity {
private String id;
private Long num;
private Integer age;
private BigDecimal price;
private Double wight;
private Date createTime;
private LocalDateTime updateTime;
private LocalDate sendDate;
}
3、比较测试
- 设置比较属性值
将实体中
age、num、price、createTime、updateTime
这五个字段的值设置为不同值;
public static void main(String[] args) throws InterruptedException {
TestEntity old = new TestEntity();
TestEntity source = new TestEntity();
// 设置一样的空值
old.setId(null);
source.setId("");
old.setAge(10);
source.setAge(22);
old.setNum(12L);
source.setNum(21L);
old.setPrice(new BigDecimal("10.00"));
source.setPrice(new BigDecimal("12.00"));
// 设置一样的值
old.setWight(123.33D);
source.setWight(123.33D);
old.setCreateTime(new Date());
old.setUpdateTime(LocalDateTime.now());
old.setSendDate(LocalDate.now());
// 故意延时,测试时间不同情况
Thread.sleep(2000);
source.setCreateTime(new Date());
source.setUpdateTime(LocalDateTime.now());
source.setSendDate(LocalDate.now());
Map<String, Object> map = FieldComparisonUtil.compareFields(old, source, false);
TestEntity target = new TestEntity();
FieldComparisonUtil.compareFieldsToNewObject(old, source, false, target);
System.out.println("完成对象比较");
}
- 测试结果
从测试结果中,可以看到
Map
集合中,有五个字段,这个五个字段就是定义实体时,设置值不同的五个字段;