在很多情况下,尤其是单表的版本控制的时候,需要记录后台人员的操作日志,通常是用到两个java对象去坐对比,告诉后台人员,改了哪些字段,就好比[名字:李四->张三],名字由原来的李四变成了张三。那么不废话了,直接亮出我反射结合注解写的对象对比工具。
import java.lang.annotation.*;
/**
* 字段比较注解
* @author liaoqian
* @since 2023/8/11
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface FiledCompara {
String value() ;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@FiledCompara("名字")
private String name;
@FiledCompara("密码")
private String password;
@FiledCompara("儿子")
private User son;
public User(String name, String password) {
this.name = name;
this.password = password;
}
}
import com.example.springboottest.annotation.FiledCompara;
import java.lang.reflect.Field;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 对象比较工具
*
* @author liaoqian
* @since 2023/8/11
*/
public class ObjectComparaUtil {
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap(8);
static {
primitiveWrapperTypeMap.put(Boolean.class, Boolean.TYPE);
primitiveWrapperTypeMap.put(Byte.class, Byte.TYPE);
primitiveWrapperTypeMap.put(Character.class, Character.TYPE);
primitiveWrapperTypeMap.put(Double.class, Double.TYPE);
primitiveWrapperTypeMap.put(Float.class, Float.TYPE);
primitiveWrapperTypeMap.put(Integer.class, Integer.TYPE);
primitiveWrapperTypeMap.put(Long.class, Long.TYPE);
primitiveWrapperTypeMap.put(Short.class, Short.TYPE);
}
/**
* 对象对比
* @param source 原始对象
* @param target 目标对象
* @return
*/
public static Map<String, String> comparaObj(Object source, Object target) {
return comparaObj(source, target, null);
}
/**
*
* @param source
* @param target
* @param map
* @return
*/
private static Map<String, String> comparaObj(Object source, Object target, Map<String, String> map) {
Map<String, String> ans = new LinkedHashMap<>(1 << 6);
if (source == null || target == null) {
throw new RuntimeException("比较的两个对象不能为空");
}
Class clazz = source.getClass();
if (!clazz.equals(target.getClass())) {
throw new RuntimeException("比较的两个对象类型不同");
}
Field[] fields = clazz.getDeclaredFields();
String format = "%s:%s->%s ";
for (Field field : fields) {
FiledCompara filedCompara = field.getAnnotation(FiledCompara.class);
if (filedCompara != null) {
field.setAccessible(true);
Object sourceFieldValue = null;
Object targetFieldValue = null;
try {
sourceFieldValue = field.get(source);
targetFieldValue = field.get(target);
} catch (Exception e) {
}
boolean notNull = sourceFieldValue != null && targetFieldValue != null;
boolean primitive = isPrimitiveType(field.getType());
if (!notNull) {
continue;
}
String entityKey = field.getName();
String value = filedCompara.value();
value = value == null || "".equals(value.trim()) ? entityKey : value;
if (primitive) {
String sourceFieldValueStr = sourceFieldValue.toString();
String targetFieldValueStr = targetFieldValue.toString();
if (!sourceFieldValueStr.equals(targetFieldValueStr)) {
String entityValue = String.format(format, value, sourceFieldValueStr, targetFieldValueStr);
ans.put(entityKey, entityValue);
}
} else {
Map<String, String> sonMap = comparaObj(sourceFieldValue, targetFieldValue, ans);
if(!sonMap.isEmpty()){
for (Map.Entry<String, String> entry : sonMap.entrySet()) {
ans.put(entityKey+"."+entry.getKey(),value+"."+entry.getValue());
}
}
}
}
}
return ans;
}
/**
* 是不是基本类型、包装类、String
* @param clazz
* @return
*/
private static boolean isPrimitiveType(Class clazz) {
boolean ans = false;
if (clazz.isPrimitive()) {
return true;
}
boolean containsKey = primitiveWrapperTypeMap.containsKey(clazz);
if (containsKey) {
return true;
}
String simpleName = clazz.getSimpleName();
switch (simpleName) {
case "String":
ans = true;
break;
default:
break;
}
return ans;
}
}
测试代码
@Test
public void tt() {
User sourceUser = new User("李四", "qwertyuiop");
User targetUser = new User("张三", "qwertyui0p");
Map<String, String> map = ObjectComparaUtil.comparaObj(sourceUser, targetUser);
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey()+" --- "+entry.getValue());
}
}
测试结果
name --- 名字:李四->张三
password --- 密码:qwertyuiop->qwertyui0p
测试代码
@Test
public void tt() {
User sourceUser = new User("张三", "qwertyuiop",new User("张三的儿子","123456",new User("张三的孙子女","666666")));
User targetUser = new User("张三", "qwertyui0p",new User("张三的儿子","12345678",new User("张三的孙子男","666666")));
Map<String, String> map = ObjectComparaUtil.comparaObj(sourceUser, targetUser);
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey()+" --- "+entry.getValue());
}
}
测试结果
password --- 密码:qwertyuiop->qwertyui0p
son.password --- 儿子.密码:123456->12345678
son.son.name --- 儿子.儿子.名字:张三的孙子女->张三的孙子男