Java比较对象属性变更差异

参考文章:https://blog.csdn.net/sunnyzyq/article/details/124603360

核心逻辑是原博主的,只是根据自己的需要做了部分调整.

变动部分

  • 支持属性字典
package com.asggo.tools.compare;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by IntelliJ IDEA.
 *
 * @author eric 2022/12/2 10:21
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Compare {
    /**
     * 字段中文名称.
     *
     * @return 字段中文名称.
     */
    String value();

    /**
     * 是否元数据 默认 false.
     *
     * @return true/false.
     */
    boolean isMeta() default false;
}

package com.asggo.tools.compare;

import lombok.Data;

/**
 * Created by IntelliJ IDEA.
 *
 * @author eric 2022/12/2 10:22
 */
@Data
public class CompareNode {
    /**
     * 字段
     */
    private String fieldKey;

    /**
     * 字段值
     */
    private Object fieldValue;

    /**
     * 字段名称
     */
    private String fieldName;
}

package com.asggo.tools.compare;

import org.springframework.lang.Nullable;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 *
 * @author eric 2022/12/2 10:09
 */
public class CompareUtils<T> {
    private static final String COMMA = ",";
    private static final String EMPTY = "";

    /**
     * 属性比较
     *
     * @param source           源数据对象.
     * @param target           目标数据对象.
     * @param ignoreProperties 忽略比较的字段.
     * @return 对应属性值的比较变化
     */
    public String compare(T source, T target, @Nullable String... ignoreProperties) {
        return compare(source, target, Map.of(), ignoreProperties);
    }


    /**
     * 属性比较
     *
     * @param source           源数据对象.
     * @param target           目标数据对象.
     * @param metaMap          元数据映射.
     * @param ignoreProperties 忽略比较的字段.
     * @return 对应属性值的比较变化
     */
    public String compare(T source, T target, Map<Object, Object> metaMap, @Nullable String... ignoreProperties) {
        if (Objects.isNull(source) && Objects.isNull(target)) {
            return EMPTY;
        }
        if (Objects.isNull(target) || Objects.isNull(metaMap)) {
            return EMPTY;
        }
        Map<String, CompareNode> sourceMap = this.getFiledValueMap(source, metaMap);
        Map<String, CompareNode> targetMap = this.getFiledValueMap(target, metaMap);
        if (sourceMap.isEmpty() && targetMap.isEmpty()) {
            return EMPTY;
        }
        // 如果源数据为空,则只显示目标数据,不显示属性变化情况
        if (sourceMap.isEmpty()) {
            return doInit(targetMap, ignoreProperties);
        }
        // 如果源数据不为空,则显示属性变化情况
        String diffText = doCompare(sourceMap, targetMap, ignoreProperties);
        if (!diffText.endsWith(COMMA)) {
            return diffText;
        }
        return diffText.substring(0, diffText.length() - 1);
    }

    private String doInit(Map<String, CompareNode> targetMap, @Nullable String... ignoreProperties) {
        StringBuilder sb = new StringBuilder();

        Collection<CompareNode> values = targetMap.values();
        int size = values.size();
        int current = 0;
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
        for (CompareNode node : values) {
            current++;
            Object o = Optional.ofNullable(node.getFieldValue()).orElse(EMPTY);
            if (Objects.nonNull(ignoreList) && ignoreList.contains(node.getFieldKey())) {
                continue;
            }
            if (o.toString().length() > 0) {
                sb.append("[").append(node.getFieldName()).append(": ").append(o).append("]");
                if (current < size) {
                    sb.append(COMMA);
                }
            }
        }
        return sb.toString();
    }

    private String doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap,
                             @Nullable String... ignoreProperties) {
        StringBuilder sb = new StringBuilder();
        Set<String> keys = sourceMap.keySet();
        int size = keys.size();
        int current = 0;
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
        for (String key : keys) {
            current++;
            CompareNode sn = sourceMap.get(key);
            CompareNode tn = targetMap.get(key);
            if (Objects.nonNull(ignoreList) && ignoreList.contains(sn.getFieldKey())) {
                continue;
            }
            String sv = Optional.ofNullable(sn.getFieldValue()).orElse(EMPTY).toString();
            String tv = Optional.ofNullable(tn.getFieldValue()).orElse(EMPTY).toString();
            // 只有两者属性值不一致时, 才显示变化情况
            if (!sv.equals(tv)) {
                sb.append(String.format("[%s: %s -> %s]", sn.getFieldName(), sv, tv));
                if (current < size) {
                    sb.append(COMMA);
                }
            }
        }
        return sb.toString();
    }

    private Map<String, CompareNode> getFiledValueMap(T t, Map<Object, Object> metaMap) {
        if (Objects.isNull(t)) {
            return Collections.emptyMap();
        }
        Field[] fields = t.getClass().getDeclaredFields();
        if (fields.length == 0) {
            return Collections.emptyMap();
        }
        Map<String, CompareNode> map = new LinkedHashMap<>();
        for (Field field : fields) {
            Compare compareAnnotation = field.getAnnotation(Compare.class);
            if (Objects.isNull(compareAnnotation)) {
                continue;
            }
            field.setAccessible(true);
            try {
                String fieldKey = field.getName();
                CompareNode node = new CompareNode();
                node.setFieldKey(fieldKey);
                node.setFieldValue(compareAnnotation.isMeta() ? metaMap.getOrDefault(field.get(t), field.get(t))
                        : field.get(t));
                node.setFieldName(compareAnnotation.value());
                map.put(field.getName(), node);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    public static void main(String[] args) {
        User u1 = new User();
        u1.setSex(1);
        u1.setName("张三");
        u1.setAge(13);

        User u2 = new User();
        u2.setSex(2);
        u2.setName("李四");
        u2.setAge(13);

        String diffText = new CompareUtils<User>().compare(u1, u2);
        System.out.println(diffText);
        diffText = new CompareUtils<User>().compare(u1, u2, Map.of(1, "男", 2, "女", 0, "未知"));
        System.out.println(diffText);
    }
}
package com.asggo.tools.compare;

import lombok.Data;

/**
 * Created by IntelliJ IDEA.
 *
 * @author eric 2022/12/2 10:42
 */
@Data
public class User {
    @Compare("姓名")
    private String name;

    @Compare("年龄")
    private Integer age;

    @Compare(value = "性别", isMeta = true)
    private Integer sex;
}

运行结果:

[姓名: 张三 -> 李四],[性别: 1 -> 2]
[姓名: 张三 -> 李四],[性别: 男 ->]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WeCube是一套开源的,一站式IT架构管理和运维管理工具,主要用于简化分布式架构IT管理,并可以通过插件进行功能扩展。 WeCube设计理念: WeCube的设计理念与IT系统生命周期管理基本一致。可以通过“六个维度和一个核心”来阐述。 一个核心:通过注册新插件持续扩展WeCube的功能,通过如下5个能力实现插件注册及协作。插件注册详见“插件注册”。 菜单布局:WeCube提供前端UI基座和前端开发规范,使各个插件的前端交互能够无缝集成到WeCube,进而形成一个统一平台。 权限模型:WeCube的权限模型提供“用户-角色-菜单”三级权限模型,并提供统一认证方案。数据权限及API权限,由插件自身控制。 流程编排:WeCube内置一套标准的BPMN流程引擎,可以通过客户自定义编排驱动插件协同工作,减少人工干预。编排设计详见“编排设计”。 数据模型:插件需要将自身需要提供给其他插件使用的数据模型注册到WeCube的统一数据模型,然后通过标准的CRUD接口提供数据访问服务。 系统参数:WeCube的全局参数、插件需要客户修改、插件需要暴露给其他插件使用的参数,需要注册到WeCube的系统参数内。 六个维度:通过定义六个维度的菜单,并将插件功能有组织性的插入这六个维度菜单中,形成对IT系统全生命周期的有效管理。 任务:汇聚多种类型任务,形成一体化的工作平台。工作内容清晰可见,轻重缓急一目了然。 设计:定义模型和规范,形成标准化设计语言。通过规范化设计及图形化展示,清晰、准确地描绘出对分布式架构的期望。 执行:通过各类自动化、标准化任务的执行,将分布式架构的期望设计变成现实存在,消除人员能力参差不齐导致的实现差异。 监测:定义全方位的监测项指标,通过持续收集监测数据,精确反映现状,并发现现实与期望的差异项。 智慧:应用机器学习等技术,赋予智慧能力。通过数学建模,制定应对监测发现的差异项的处理策略。 调整:通过执行处理策略来不断进行动态调整,最终保持现实与期望的对等,进而使系统稳定运行。 WeCube主要功能简介: WeCube的功能菜单设计与设计理念保持一致。分别是任务、设计、执行、监测、智慧、调整、协同、系统。 系统 1、系统参数:管理WeCube Platform运行所需的系统参数。 2、资源管理:管理WeCube提供的资源如容器母机及资源上运行的实例。 3、权限管理:管理WeCube Platform的用户,角色和菜单,可以对权限进行菜单级别的管控。 协同 1、插件注册:选择插件包上传,插件包需声明本插件的依赖、所需菜单、数据模型、系统参数、权限设定及运行资源,注册后通过容器运行,支持多实例,可以查看插件运行的日志。选择插件服务,通过插件运行的参数关联CMDB数据模型的属性值,形成注册。 2、任务编排:比如设计一个VPC创建的编排。包括创建VPC、创建子网、创建VM。流程的每个执行节点需要关联插件。 设计(WeCMDB插件提供) 1、规划设计:用于设计机房结构。 2、资源规划:用于实例化一个机房,特别是两地三中心结构。 3、应用架构设计:用于设计一个应用的逻辑架构。 4、应用部署设计:用于实际部署一个应用。支持灰度发布。 5、CI数据管理/查询:通过模型图形进入单个数据管理以及查询。 6、CI综合查询管理/数据综合查询:用于配制多CI属性报表。比如一个应用使用到了哪些主机。 7、枚举数据管理/查询:通过对公共枚举和私有枚举进行管理以及查询。 执行 1、编排任务执行:在选择编排后可对选择目标对象执行编排,支持灰度操作。比如重启某个资源集的5台主机。 2、物料管理(Artifacts插件提供):管理应用程序的包。可以定义各种文件,可以配置环境差异导致的变量替换规则,不需要人工处理。 3、批量执行:通过配置综合查询并选择目标。在通过特定插件来执行任务。比如某个应用的所有主机,执行一个用户权限变更。 任务(Service-Management插件提供) 1、模板管理:服务目录管理, 服务请求模板管理; 2、服务管理:服务请求管理,任务管理; 监测(Open-Monitor插件提供) 1、Agent管理: 注册、启动、停止; 2、数据管理: 提供数据采集配置, 数据查询等功能; 3、告警管理: 提供阈值配置、日志监控、告警触发等功能; 4、视图管理: 提供图形配置和自定义视图功能; 调整(规划中) 智慧(规划中)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值