Skip to content
 
 

Latest commit

 

History

33 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

java-obj-diff

注意:本项目未发布到maven中央仓库,需要手动添加到本地仓库 或者 到私有仓库中使用。

比较对象差异工具类。主要用于比较同一个类的两个对象属性值的差异。

此工具是建立在

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

中的DiffBuilder基础上进行的重新开发和封装。 主要是解决一些bug.和加入了一些缓存提高比较时频繁反射的性能。同时增加了一些新特性。

特性

属性忽略比较

注解支持

使用 @DiffIgnore 注解可以忽略某个属性的比较。

@DiffIgnore
private String name;
手动忽略

调用工具类DiffUtils 时传递排除比较的字段名。

DiffResult diffResult = DiffUtils.diff(obj1, obj2, "name");

自定义比较器

使用 @DiffCompare 注解可以自定义某个属性的比较器。 自定义比较器需要实现 DiffComparable 接口。

public class MyComparator implements DiffComparable {
    @Override
    public boolean diff(Object obj1, Object obj2) {
        return obj1.equals(obj2);
    }
}

如果自定义比较器可以使用无参构造实例化,可以不用手动注册到工厂中。否则用户需要自定义调用com.openquartz.javaobjdiff.DiffComparableFactory.register方法注入到工厂中。

使用自定义比较器

@DiffCompare(using = MyComparator.class)
private String name;

内置比较器

库内置了几个可直接复用的比较器,无需自己实现:

  • DefaultDiffComparator:默认比较器,使用 equals 比较。
  • BigDecimalEffectiveDiffComparator:BigDecimal 忽略尾零的有效值比较,1.0 与 1.00 视为相等。
@DiffCompare(using = BigDecimalEffectiveDiffComparator.class)
private BigDecimal money;

自定义类型属性

支持自定义成员变量需要使用注解标识。会自动解析到属性 可以加注解@DiffBean来标识。

public class User {
    private String name;
    private int age;
    private Date birthday;
    @DiffBean
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }
    
    public void setAddress(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return this.address;
    }
}

@DiffBean注解也支持加在 Collection及其子类上。并支持到Collection的元素类型为自定义类型 例如:

 @DiffBean
private List<Packet> packetList;

@DiffBean
private List<Integer> friendIdList;

别名支持

使用 @DiffAlias 注解可以自定义某个属性的别名。

@DiffAlias(alias = "用户名")
private String name;

输出报告中会显示别名。否则默认使用字段属性名。

格式化支持

使用 @DiffFormat 注解可以自定义某个属性的格式化。在输出结果中展示格式化后的数据。

@DiffFormat(pattern = "yyyy-MM-dd", using=DateTimeDiffFormatter.class)
private Date birthday;

也可以自定义格式化器。需要实现接口:com.openquartz.javaobjdiff.DiffFormatter

例如:

public class DateTimeDiffFormatter implements DiffFormatter {
    
    @Override
    public String format(Object obj) {
        
        if (StringUtils.isBlank(pattern)) {
            pattern = DEFAULT_FORMATTER_PATTERN;
        }

        if (Objects.isNull(value)) {
            return null;
        }

        if (value instanceof Date) {
            return DateFormatUtils.format((Date) value, pattern);
        }

        if (value instanceof TemporalAccessor) {
            return DateTimeFormatter.ofPattern(pattern).format((LocalDateTime) value);
        }

        throw new IllegalArgumentException("Unsupported type: " + value.getClass());
    }
}

使用场景:自定义类型、枚举类型等。可自己实现自定义格式化方式。

使用示例代码详见:com.openquartz.javaobjdiff.test.DiffUtilTest

脱敏支持

对于手机号、邮箱、身份证号等敏感字段,在输出差异结果时支持自动脱敏展示,避免敏感信息在 diff 结果中明文泄露。

支持以下三种内置脱敏注解:

注解 适用字段 脱敏规则 示例
@EmailDataMask 邮箱 前缀仅保留首字母,其余用 * 替代,保留 @ 及域名 [email protected] → ***********[email protected]
@MobileDataMask 手机号 / 座机 手机号保留前 3 位与后 4 位;座机保留区号与后 4 位 17000000056 → 170****0056;010-12345678 → 010-****5678
@IdCardDataMask 身份证号 保留前 6 位与后 4 位(仅对 18 位生效) 321556199301152110 → 321556********2110

用法示例:

public class User {
    @MobileDataMask
    private String mobile;

    @EmailDataMask
    private String email;

    @IdCardDataMask
    private String idCard;
}

diff 输出示例:

User[mobile=170****0056, email=***********[email protected], idCard=321556********2110] -> User[mobile=171****0056, email=*********[email protected], idCard=301556********2110]

注意:脱敏仅作用于 DiffResult.toString() / Diff.getFormatValue() 等输出展示环节,字段的比较逻辑仍基于原始值。两个对象该字段原始值不同,即使脱敏后的展示串相同,仍会被判定为有差异。

结构化 diff 结果

DiffUtils.diff(...) 返回格式化的字符串;如果需要以编程方式获取结构化结果,使用 DiffUtils.diffResult(...):

DiffResult<Person> result = DiffUtils.diffResult(person1, person2);

boolean diff = result.isDiff();               // 是否存在差异
int count = result.getNumberOfDiffs();        // 差异数量
List<Diff<?>> diffs = result.getDiffs();      // 差异列表(不可修改)

for (Diff<?> d : result) {                    // DiffResult 实现了 Iterable<Diff<?>>
    String field = d.getFieldName();          // 字段路径,如 address.country
    String name  = d.getActualName();         // 展示名(别名优先)
    Object left  = d.getLeft();               // 左侧(旧)值
    Object right = d.getRight();              // 右侧(新)值
    Object formatLeft  = d.getFormatValue(left);   // 格式化/脱敏后的展示值
}

输出前缀与自定义样式

// 为输出中的所有字段添加前缀
String diff = DiffUtils.diff(source, target, "user", "excludeField1", "excludeField2");

// 使用自定义 ToStringStyle
String diff = DiffUtils.diff(source, target, new MyToStringStyle(), "excludeField1");

其他行为说明

  • static、transient 字段不参与比较。
  • 字段反射结果按 Class 缓存,频繁比较同一类型的对象时性能更好。
  • 默认使用 equals / 引用相等进行比较,可通过 @DiffIgnore、@DiffCompare、@DiffBean、@DiffFormat 等注解覆盖默认行为。

About

功能强大的java比较对象差异工具类

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages