JAVA 使用反射比较对象属性的变化,记录修改日志。使用注解【策略模式】,来进行不同属性枚举值到中英文描述的切换,支持前端国际化。

news2025/1/27 2:07:25

1.首先定义一个接口,接口中有两个方法,分别是将属性转换成英文描述和中文描述。

其实就是将数据库中记录的 0  1 ,转换成后面的描述

这边定义了中文转换为默认方法,是因为有些属性不需要进行中文转换,或者该属性的枚举值中没有中文描述,你也可以不定义为默认方法

public interface ColumnConverter {
    /**
     * 英文值
     */
    Object enConverter(Object value);

    /**
     * 中文值
     */
    default Object cnConverter(Object value) {
        return enConverter(value);
    }
}

2.然后我们就可以定义一个类,该类中有很多静态类【不同的静态类用于不同属性的枚举值转换】,代码如下

@Slf4j
public class ColumnStrategy {
    private final Map<Class<? extends ColumnConverter>, ColumnConverter> converterMap = new HashMap<>();

    private ColumnStrategy() {
    }

    /**
     * 获取单例
     */
    public static ColumnStrategy getInstance() {
        return INSTANCE.Instance;
    }

    public ColumnConverter getConverter(Class<? extends ColumnConverter> converterClass) {
        try {
            if (converterMap.containsKey(converterClass)) {
                return converterMap.get(converterClass);
            }
            Constructor<? extends ColumnConverter> constructor = converterClass.getConstructor();
            ColumnConverter columnConverter = constructor.newInstance();
            converterMap.put(converterClass, columnConverter);
            return columnConverter;
        } catch (Exception e) {
            log.error("构造转换器对象异常", e);
            return null;
        }
    }

    public ColumnConverter getDefaultConverter() {
        ColumnConverter defaultConverter = converterMap.get(AutoConverter.class);
        if (defaultConverter != null) {
            return defaultConverter;
        }
        AutoConverter autoConverter = new AutoConverter();
        converterMap.put(AutoConverter.class, autoConverter);
        return autoConverter;
    }

    private static class INSTANCE {
        private static final ColumnStrategy Instance = new ColumnStrategy();
    }

    /**
     * 默认转换器
     */
    public static class AutoConverter implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return value;
        }
    }

    

    
    public static class CarrierLevelType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> CarrierTypeEnum.getByCode((int) e))
                    .map(CarrierTypeEnum::getEnDesc)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> CarrierTypeEnum.getByCode((int) e))
                    .map(CarrierTypeEnum::getDescription)
                    .orElse(null);
        }
    }


    /**
     * 结果集转换器
     * 将<转换为&lt; (前端要求)
     */
    public static class OperationDescResultConverter implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return ((String) value).replaceAll(KeyboardSpecialCharConstants.LESS, KeyboardSpecialCharConstants.LESS_ESCAPING);
        }
    }


    public static class BusinessStatusType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> BusinessStatusEnum.getByCode((int) e))
                    .map(BusinessStatusEnum::getValue)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> BusinessStatusEnum.getByCode((int) e))
                    .map(BusinessStatusEnum::getCnValue)
                    .orElse(null);
        }
    }






    public static class PriceCheckType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> PriceCheckModeEnum.getEnumByCode((int) e))
                    .map(PriceCheckModeEnum::getModeDescEn)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> PriceCheckModeEnum.getEnumByCode((int) e))
                    .map(PriceCheckModeEnum::getModeDesc)
                    .orElse(null);
        }
    }



    public static class RealPriceCalType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> RealPriceCalMethodEnum.getEnumByCode((int) e))
                    .map(RealPriceCalMethodEnum::getMethodDescEn)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> RealPriceCalMethodEnum.getEnumByCode((int) e))
                    .map(RealPriceCalMethodEnum::getMethodDesc)
                    .orElse(null);
        }
    }



}

3.然后我们定义一个注解,该注解用于我们实际进行比较的类中上,具体值是否需要进行枚举值转换,以及字段的中英文名称

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnInfo {
    /**
     * 字段中文名
     */
    String cnName() default "";

    /**
     * 字段英文名
     */
    String enName() default "";

    /**
     * 值转换器(英文)
     * 适用于枚举型转换
     */
    Class<? extends ColumnConverter> converter() default ColumnStrategy.AutoConverter.class;

    /**
     * 是否是用户数组
     */
    boolean isUserList() default false;
}

4.然后我们就可以在实际需要进行比较的类上加上该注解【需要进行枚举值转换的属性,我们可以在属性上面的注解中加上converter ,然后注入对应的转换器即可】,示例代码如下

@Data
public class DTO {


    /**
     * 业务名称
     */
    @ColumnInfo(cnName = "业务名称", enName = "Business name")
    private String bizCode;



    /**
     * 国别
     */
    @ColumnInfo(cnName = "国家", enName = "Country")
    private String country;



    /**
     * 国别
     */
    @ColumnInfo(cnName = "到期时间", enName = "Expire time")
    private String expireTime;


    /**
     * 业务状态
     */
    @ColumnInfo(cnName = "业务状态", enName = "status", converter = ColumnStrategy.BusinessStatusType.class)
    private Integer status;



    /**
     * 是否校验价格
     */
    @ColumnInfo(cnName = "是否校验价格", enName = "Is check price", converter = ColumnStrategy.PriceCheckType.class)
    private Integer priceCheckMode;



}

5.现在就可以直接传入修改前后的两个对象,利用反射对其进行修改值的检测了

//调用示例
getChangeFields(DTO.class, from, to, descCnList, descEnList);    


//具体方法代码如下
private void getChangeFields(Class clazz, Object obj1, Object
            obj2, ArrayList<String> cnList, ArrayList<String> enList) {
        try {
            // 解析对象1和对象2的JSONObject
            JSONObject object1 = JSONUtil.parseObj(obj1);
            JSONObject object2 = JSONUtil.parseObj(obj2);
            if (object1.isEmpty() || object2.isEmpty()) {
                return;
            }
            // 获取该类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 设置属性可访问
                field.setAccessible(true);
                // 获取属性名
                String name = field.getName();
                ColumnInfo targetColumnInfo = field.getAnnotation(ColumnInfo.class);
                ColumnConverter columnConverter = getColumnConverter(targetColumnInfo);
                // 判断对象1和对象2的属性数量是否不为0(如果是创建则object2为null)
                // 判断对象1和对象2的属性值是否都不为空(由于有一些属性始终为null,所以需要过滤掉,不然会空指针异常)
                Object o1 = object1.get(name);
                Object o2 = object2.get(name);
                // 判断对象1和对象2的属性值是否不相等
                if (ObjectUtil.equals(o1, o2)) {
                    continue;
                }
                if (Constants.CHECK_CONVERT_FILED.contains(name)) {
                    Object cnFrom = ObjectUtil.isNotEmpty(o1) ? columnConverter.cnConverter(o1) : StringPool.EMPTY;
                    Object cnTo = ObjectUtil.isNotEmpty(o2) ? columnConverter.cnConverter(o2) : StringPool.EMPTY;
                    Object enFrom = ObjectUtil.isNotEmpty(o1) ? columnConverter.enConverter(o1) : StringPool.EMPTY;
                    Object enTo = ObjectUtil.isNotEmpty(o2) ? columnConverter.enConverter(o2) : StringPool.EMPTY;
                    // 添加属性名到列表中
                    cnList.add(String.format("%s【%s】更新为【%s】", targetColumnInfo.cnName(), cnFrom, cnTo));
                    enList.add(String.format("%s【%s】 changed to 【%s】", targetColumnInfo.enName(), enFrom, enTo));
                } else {
                    o1 = ObjectUtil.isNotEmpty(o1) ? o1 : StringPool.EMPTY;
                    o2 = ObjectUtil.isNotEmpty(o2) ? o2 : StringPool.EMPTY;
                    if (ObjectUtil.isNotEmpty(targetColumnInfo.cnName())) {
                        cnList.add(String.format("%s【%s】更新为【%s】", targetColumnInfo.cnName(), o1, o2));
                    }
                    if (ObjectUtil.isNotEmpty(targetColumnInfo.enName())) {
                        enList.add(String.format("%s【%s】 changed to 【%s】", targetColumnInfo.enName(), o1, o2));
                    }
                }

            }
        } catch (Exception e) {
            // 获取异常信息详情
            log.error("---Failed to check attributes---", e);
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2283715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于模糊PID的孵化箱温度控制系统(论文+源码)

1系统方案设计 本课题为基于模糊PID的孵化箱温度控制系统&#xff0c;其以STM32最小系统与模糊PID控制器为控制核心。系统主要包括数据采集模块、处理器模块、电机控制模块。 数据采集模块由温度传感器构成&#xff0c;通过温度传感器感应温度变化&#xff0c;获得待处理的数据…

机器学习-数据集划分

文章目录 一. 为什么要划分数据集二. 数据集划分的方法1. 留出法&#xff1a;2. 交叉验证&#xff1a;将数据集划分为训练集&#xff0c;验证集&#xff0c;测试集3. 留一法&#xff1a;4. 自助法&#xff1a; 一. 为什么要划分数据集 为了能够评估模型的泛化能力&#xff0c;可…

Hive之加载csv格式数据到hive

场景&#xff1a; 今天接了一个需求&#xff0c;将测试环境的hive数据导入到正式环境中。但是不需要整个流程的迁移&#xff0c;只需要迁移ads表 解决方案&#xff1a; 拿到这个需求首先想到两个方案&#xff1a; 1、将数据通过insert into语句导出&#xff0c;然后运行脚本 …

C# OpenCV机器视觉:利用CNN实现快速模板匹配

在一个阳光灿烂的周末&#xff0c;阿强正瘫在沙发上&#xff0c;百无聊赖地换着电视频道。突然&#xff0c;一则新闻吸引了他的注意&#xff1a;某博物馆里一幅珍贵的古画离奇失踪&#xff0c;警方怀疑是被一伙狡猾的盗贼偷走了&#xff0c;现场只留下一些模糊不清的监控画面&a…

WinRAR.exe命令行的使用

工具 命令行打包命令 rem 默认压缩根目录&#xff0c;递归处理子文件夹使用 -r WinRAR.exe a -r test.rar C:/web/Views/

【更正版】梯级水光互补系统最大化可消纳电量期望短期优化调度模型

目录 1 主要内容 目标函数&#xff1a; 约束条件&#xff1a; 线性化处理&#xff1a; 流程示意&#xff1a; 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《梯级水光互补系统最大化可消纳电量期望短期优化调度模型》&#xff0c;构建了以最大化整体可…

移动端VR处理器和传统显卡的不同

骁龙 XR 系列芯片 更多地依赖 AI 技术 来优化渲染过程&#xff0c;而传统的 GPU 渲染 则倾向于在低画质下运行以减少负载。这种设计是为了在有限的硬件资源下&#xff08;如移动端 XR 设备&#xff09;实现高性能和低功耗的平衡。以下是具体的分析&#xff1a; 1. AI 驱动的渲染…

基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真。选择回归法进行最大功率点的追踪&#xff0c;使用光强和温度作为影响因素&#xff0c;电压作为输出进行建模。…

JVM深入学习(一)

目录 一.JVM概述 1.1 为什么要学jvm&#xff1f; 1.2 jvm的作用 1.3 jvm内部构造 二.JVM类加载 2.1类加载过程 2.2类加载器 2.3类加载器的分类 2.4双亲委派机制 三.运行时数据区 堆空间区域划分&#xff08;堆&#xff09; 为什么分区(代)&#xff1f;&#xff08…

【精选】基于数据挖掘的招聘信息分析与市场需求预测系统 职位分析、求职者趋势分析 职位匹配、人才趋势、市场需求分析数据挖掘技术 职位需求分析、人才市场趋势预测

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

视觉语言模型 (VLMs):跨模态智能的探索

文章目录 一. VLMs 的重要性与挑战&#xff1a;连接视觉与语言的桥梁 &#x1f309;二. VLMs 的核心训练范式&#xff1a;四种主流策略 &#x1f5fa;️1. 对比训练 (Contrastive Training)&#xff1a;拉近正例&#xff0c;推远负例 ⚖️2. 掩码方法 (Masking)&#xff1a;重构…

CF 339A.Helpful Maths(Java实现)

题目分析 输入一串式子&#xff0c;输出从小到大排列的式子 思路分析 如上所说核心思路&#xff0c;但是我要使用笨方法&#xff0c;输入一串式子用split分割开&#xff0c;但是此时需要用到转义字符&#xff0c;即函数内参数不能直接使用“”&#xff0c;而是“\\”。分割开后…

Java Web-Request与Response

在 Java Web 开发中&#xff0c;Request 和 Response 是两个非常重要的对象&#xff0c;用于在客户端和服务器之间进行请求和响应的处理&#xff0c;以下是详细介绍&#xff1a; Request&#xff08;请求对象&#xff09; Request继承体系 在 Java Web 开发中&#xff0c;通…

Spring AOP通知类型全解析:掌握方法执行前后的艺术

Spring的通知&#xff08;Advice&#xff09;类型主要有以下几种&#xff0c;它们都是在方法执行的不同阶段进行拦截和处理的一种机制&#xff1a; 1. 前置通知&#xff08;Before Advice&#xff09;&#xff1a;在目标方法执行之前执行的通知。就像你吃饭前要先洗手一样&…

(一)HTTP协议 :请求与响应

前言 爬虫需要基础知识&#xff0c;HTTP协议只是个开始&#xff0c;除此之外还有很多&#xff0c;我们慢慢来记录。 今天的HTTP协议&#xff0c;会有助于我们更好的了解网络。 一、什么是HTTP协议 &#xff08;1&#xff09;定义 HTTP&#xff08;超文本传输协议&#xff…

未初始化数据恢复全攻略

没有初始化概述 在日常使用电脑、硬盘、U盘等存储设备时&#xff0c;我们可能会遇到“没有初始化”的提示。这一情况通常发生在存储设备突然无法被系统正常识别或访问时&#xff0c;系统往往要求我们先进行初始化操作。然而&#xff0c;初始化操作意味着对存储设备进行格式化&…

学习数据结构(1)算法复杂度

1.数据结构和算法 &#xff08;1&#xff09;数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在⼀种或多种特定关系的数据元素的集合 &#xff08;2&#xff09;算法就是定义良好的计算过程&#xff0c;取一个或一组的值为输入&#xff0c;并产生出一个或一组…

Github 2025-01-25Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-01-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目1Vue项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型…

免费GPU算力,不花钱部署DeepSeek-R1

在人工智能和大模型技术飞速发展的今天&#xff0c;越来越多的开发者和研究者希望能够亲自体验和微调大模型&#xff0c;以便更好地理解和应用这些先进的技术。然而&#xff0c;高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是&#xff0c;腾讯云Cloud Studio提供了免…

積分方程與簡單的泛函分析7.希爾伯特-施密特定理

1)def函數叫作"由核生成的(有源的)" 定义: 设 是定义在区域上的核函数。 对于函数,若存在函数使得, 则称函数是“由核生成的(有源的)”。 这里的直观理解是: 函数的“来源”可以通过核函数 与另一个函数的积分运算得到。 在积分方程理论中,这种表述常…