开发中经常遇到业务提出一些日志变更记录,例如用户修改了哪些字段,又或者B系统调用我方修改了表的哪些字段,为了节省时间省略 is else判断,大概写个对象变更工具类。
本文介绍了一个对象变更对比工具类的实现方案,主要用于记录业务数据变更日志。核心功能包括:
1)通过自定义@FieldName注解为字段添加中文描述;
2)利用反射机制比较两个对象字段差异;
3)输出带中文注释的变更记录。该工具类可自动识别被@FieldName注解标记的字段,返回字段名、中文描述及变更前后的值,适用于系统间接口调用或用户操作日志等场景。示例展示了如何定义实体类、使用注解及调用比较方法,最终输出易读的变更记录。该方案简化了日志开发工作,提高了可维护性。
1、自定义@FieldName注解(通过注解获取中文注释)
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldName {
String value(); // 字段的中文描述
}
2、创建一个 对象对比工具类
import java.lang.reflect.Field;
import java.util.*;
public class ObjectCompareUtil {
/**
* 比较两个对象的字段差异,并返回带中文注释的变更记录
*/
public static List<FieldChange> compareFieldsWithChinese(Object oldObj, Object newObj) {
List<FieldChange> changes = new ArrayList<>();
if (oldObj == null || newObj == null || !oldObj.getClass().equals(newObj.getClass())) {
return changes;
}
Field[] fields = oldObj.getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
Object oldValue = field.get(oldObj);
Object newValue = field.get(newObj);
if (isFieldChanged(oldValue, newValue)) {
String chineseName = getFieldChineseName(field);
changes.add(new FieldChange(
field.getName(),
chineseName, // 添加中文注释
oldValue,
newValue
));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return changes;
}
/**
* 获取字段的中文注释(通过 @FieldName 注解)
*/
private static String getFieldChineseName(Field field) {
FieldName annotation = field.getAnnotation(FieldName.class);
return annotation != null ? annotation.value() : field.getName();
}
/**
* 判断字段值是否发生变化
*/
private static boolean isFieldChanged(Object oldValue, Object newValue) {
return (oldValue == null && newValue != null) ||
(oldValue != null && !oldValue.equals(newValue));
}
/**
* 字段变更记录类(增强版)
*/
public static class FieldChange {
private String fieldName; // 字段英文名
private String fieldChinese; // 字段中文名
private Object oldValue;
private Object newValue;
public FieldChange(String fieldName, String fieldChinese, Object oldValue, Object newValue) {
this.fieldName = fieldName;
this.fieldChinese = fieldChinese;
this.oldValue = oldValue;
this.newValue = newValue;
}
// Getters
public String getFieldName() { return fieldName; }
public String getFieldChinese() { return fieldChinese; }
public Object getOldValue() { return oldValue; }
public Object getNewValue() { return newValue; }
@Override
public String toString() {
return String.format(
"字段【%s】从 '%s' 修改为 '%s'",
fieldChinese, oldValue, newValue
);
}
}
}
3、在对象上使用注解
import cn.com.xxx.common.core.web.domain.BaseEntity;
import cn.com.xxx.common.security.utils.FieldName;
import lombok.Data;
/**
* 产品版本信息
*/
@Data
public class ProdVersion {
/**
* 版本状态
*/
private String status;
/**
* 是否删除:0未删除 1已删除
*/
@FieldName("是否删除")
private String isDel;
}
4、使用方法参考
public static void main(String[] args) {
ProdVersion oldPo = new ProdVersion();
oldPo.setStatus("0");
oldPo.setIsDel("N");
ProdVersion newPo = new ProdVersion();
newPo.setStatus("1");
newPo.setIsDel("NYY");
List<ObjectCompareUtil.FieldChange> fieldChanges = ObjectCompareUtil.compareFieldsWithChinese(oldPo, newPo);
System.out.println(fieldChanges.toString());
}
运行输出结果:[字段【status】从 '0' 修改为 '1', 字段【是否删除】从 'N' 修改为 'Y']