| | |
| | | import java.lang.reflect.Modifier; |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.lang.reflect.Type; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.apache.commons.lang3.Validate; |
| | | import org.apache.poi.ss.usermodel.DateUtil; |
| | |
| | | } |
| | | return new RuntimeException(msg, e); |
| | | } |
| | | |
| | | /** |
| | | * 比较两个相同类型对象的所有字段值是否相等 |
| | | * 使用反射获取所有字段(包括父类字段),并逐个比较 |
| | | * |
| | | * @param obj1 第一个对象 |
| | | * @param obj2 第二个对象 |
| | | * @return 如果所有字段值都相等返回true,否则返回false |
| | | * *示例1:比较所有字段 |
| | | * * boolean result = ReflectUtils.equalsAllFields(user1, user2); |
| | | */ |
| | | public static boolean equalsAllFields(Object obj1, Object obj2) |
| | | { |
| | | return equalsAllFields(obj1, obj2, new String[]{}); |
| | | } |
| | | |
| | | /** |
| | | * 比较两个相同类型对象的所有字段值是否相等(可排除指定字段) |
| | | * 使用反射获取所有字段(包括父类字段),并逐个比较 |
| | | * |
| | | * @param obj1 第一个对象 |
| | | * @param obj2 第二个对象 |
| | | * @param excludeFields 要排除的字段名称数组 |
| | | * @return 如果所有字段值都相等返回true,否则返回false |
| | | * // 示例2:排除分页参数 |
| | | * * boolean result = ReflectUtils.equalsAllFields(vo1, vo2, "pageNum", "pageSize"); |
| | | */ |
| | | public static boolean equalsAllFields(Object obj1, Object obj2, String... excludeFields) |
| | | { |
| | | // 两个对象都为null,认为相等 |
| | | if (obj1 == null && obj2 == null) return true; |
| | | |
| | | // 只有一个为null,不相等 |
| | | if (obj1 == null || obj2 == null) return false; |
| | | |
| | | // 如果是同一个对象引用,直接返回true |
| | | if (obj1 == obj2) return true; |
| | | |
| | | // 类型不同,不相等 |
| | | if (!obj1.getClass().equals(obj2.getClass())) return false; |
| | | |
| | | // 将排除字段转换为Set,便于快速查找 |
| | | Set<String> excludeFieldSet = new HashSet<>(Arrays.asList(excludeFields)); |
| | | |
| | | // 获取所有字段(包括父类字段) |
| | | List<Field> allFields = getAllFields(obj1); |
| | | |
| | | // 逐个比较字段值 |
| | | for (Field field : allFields) |
| | | { |
| | | // 跳过排除的字段 |
| | | if (excludeFieldSet.contains(field.getName())) continue; |
| | | |
| | | // 跳过static和transient字段 |
| | | if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | try{ |
| | | makeAccessible(field); |
| | | Object value1 = field.get(obj1); |
| | | Object value2 = field.get(obj2); |
| | | |
| | | // 使用Objects.equals进行比较,可以正确处理null值 |
| | | if (!Objects.equals(value1, value2)) |
| | | { |
| | | //logger.debug("字段 [{}] 的值不相等: obj1={}, obj2={}", field.getName(), value1, value2); |
| | | return false; |
| | | } |
| | | }catch (IllegalAccessException e) |
| | | { |
| | | //logger.error("访问字段 [{}] 时发生错误: {}", field.getName(), e.getMessage()); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 获取对象的所有字段(包括父类字段) |
| | | * |
| | | * @param obj 目标对象 |
| | | * @return 所有字段的列表 |
| | | */ |
| | | public static List<Field> getAllFields(Object obj) |
| | | { |
| | | List<Field> allFields = new ArrayList<>(); |
| | | Class<?> clazz = obj.getClass(); |
| | | |
| | | // 循环向上遍历父类,获取所有字段 |
| | | while (clazz != null && clazz != Object.class) |
| | | { |
| | | Field[] fields = clazz.getDeclaredFields(); |
| | | allFields.addAll(Arrays.asList(fields)); |
| | | clazz = clazz.getSuperclass(); |
| | | } |
| | | |
| | | return allFields; |
| | | } |
| | | |
| | | /** |
| | | * 比较两个对象指定字段的值是否相等 |
| | | * |
| | | * @param obj1 第一个对象 |
| | | * @param obj2 第二个对象 |
| | | * @param includeFields 要比较的字段名称数组 |
| | | * @return 如果指定字段值都相等返回true,否则返回false |
| | | * |
| | | * // 示例3:只比较关键字段 |
| | | * boolean result = ReflectUtils.equalsSpecifiedFields(user1, user2, "userId", "userName", "email"); |
| | | */ |
| | | public static boolean equalsSpecifiedFields(Object obj1, Object obj2, String... includeFields) |
| | | { |
| | | // 两个对象都为null,认为相等 |
| | | if (obj1 == null && obj2 == null) return true; |
| | | |
| | | // 只有一个为null,不相等 |
| | | if (obj1 == null || obj2 == null) return false; |
| | | |
| | | // 如果是同一个对象引用,直接返回true |
| | | if (obj1 == obj2) return true; |
| | | |
| | | // 类型不同,不相等 |
| | | if (!obj1.getClass().equals(obj2.getClass())) return false; |
| | | |
| | | // 如果没有指定字段,返回true |
| | | if (includeFields == null || includeFields.length == 0) return true; |
| | | |
| | | // 逐个比较指定字段的值 |
| | | for (String fieldName : includeFields) |
| | | { |
| | | Field field = getAccessibleField(obj1, fieldName); |
| | | if (field == null) |
| | | { |
| | | //logger.warn("在 [{}] 中未找到字段 [{}]", obj1.getClass().getName(), fieldName); |
| | | continue; |
| | | } |
| | | |
| | | try |
| | | { |
| | | makeAccessible(field); |
| | | Object value1 = field.get(obj1); |
| | | Object value2 = field.get(obj2); |
| | | |
| | | // 使用Objects.equals进行比较,可以正确处理null值 |
| | | if (!Objects.equals(value1, value2)) |
| | | { |
| | | //logger.debug("字段 [{}] 的值不相等: obj1={}, obj2={}", fieldName, value1, value2); |
| | | return false; |
| | | } |
| | | } |
| | | catch (IllegalAccessException e) |
| | | { |
| | | //logger.error("访问字段 [{}] 时发生错误: {}", fieldName, e.getMessage()); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |