当前位置: 首页 > news >正文

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

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 {@Overridepublic Object enConverter(Object value) {return value;}}public static class CarrierLevelType implements ColumnConverter {@Overridepublic Object enConverter(Object value) {return Optional.ofNullable(value).map(e -> CarrierTypeEnum.getByCode((int) e)).map(CarrierTypeEnum::getEnDesc).orElse(null);}@Overridepublic 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 {@Overridepublic Object enConverter(Object value) {return ((String) value).replaceAll(KeyboardSpecialCharConstants.LESS, KeyboardSpecialCharConstants.LESS_ESCAPING);}}public static class BusinessStatusType implements ColumnConverter {@Overridepublic Object enConverter(Object value) {return Optional.ofNullable(value).map(e -> BusinessStatusEnum.getByCode((int) e)).map(BusinessStatusEnum::getValue).orElse(null);}@Overridepublic 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 {@Overridepublic Object enConverter(Object value) {return Optional.ofNullable(value).map(e -> PriceCheckModeEnum.getEnumByCode((int) e)).map(PriceCheckModeEnum::getModeDescEn).orElse(null);}@Overridepublic 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 {@Overridepublic Object enConverter(Object value) {return Optional.ofNullable(value).map(e -> RealPriceCalMethodEnum.getEnumByCode((int) e)).map(RealPriceCalMethodEnum::getMethodDescEn).orElse(null);}@Overridepublic 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, Objectobj2, ArrayList<String> cnList, ArrayList<String> enList) {try {// 解析对象1和对象2的JSONObjectJSONObject 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);}}

相关文章:

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

1.首先定义一个接口&#xff0c;接口中有两个方法&#xff0c;分别是将属性转换成英文描述和中文描述。 其实就是将数据库中记录的 0 1 &#xff0c;转换成后面的描述 这边定义了中文转换为默认方法&#xff0c;是因为有些属性不需要进行中文转换&#xff0c;或者该属性的枚举…...

Docker入门学习

一、容器 1. 将单个操作系统中的资源划分到孤立的组中&#xff0c;在孤立的组中平衡有冲突的资源使用需求 2. Docker提供了容器管理的工具可以无需关注底层操作&#xff0c;使用效果类似于轻量级的虚拟机&#xff0c; 并且容器的创建和停止相对于虚拟机来说比较快&am…...

吴恩达深度学习——神经网络介绍

文章内容来自BV11H4y1F7uH&#xff0c;仅为个人学习所用。 文章目录 什么是神经网络引入神经网络神经元激活函数ReLU隐藏单元 用神经网络进行监督学习监督学习与无监督学习举例 什么是神经网络 引入 已经有六个房子的数据集&#xff0c;横轴为房子大小&#xff0c;纵轴为房子…...

STM32之CubeMX新建工程操作(十八)

STM32F407 系列文章 - STM32CubeMX&#xff08;十八&#xff09; 目录 前言 一、STM32CubeMX 二、新建工程 ​编辑 1.创建工程 2.选择芯片型号 3.Pinout引脚分配 1.SYS配置 2.RCC配置 3.定时器配置 4.GPIO引脚配置 5.中断配置 6.通讯接口配置 7.插件Middleware配…...

Postgresql源码(140)理解PG的编译流程(make、Makefile、Makefile.global.in)

PG16 PG中使用的makefile看起来代码比较多&#xff0c;但是实际逻辑比较简单&#xff0c;这里做一些抽象总结。 总结 Makefile.global.in的$(recurse)宏自动生成了target&#xff0c;可以方便的进入内存目录进行编译。 all: all-common-recurse all-common-recurse: submak…...

logback日志自定义占位符

前言 在大型系统运维中&#xff0c;很大程度上是需要依赖日志的。在java大型web工程中&#xff0c;一般都会使用slf4jlogback这一个组合来实现日志的管理。 logback中很多现成的占位符可以可以直接使用&#xff0c;比如线程号【%t】、时间【%d】、日志等级【%p】&#xff0c;…...

Vue平台开发三——项目管理页面

前言 对于多个项目的使用&#xff0c;可能需要进行项目切换管理&#xff0c;所以这里创建一个项目管理页面&#xff0c;登录成功后跳转这个页面&#xff0c;进行选择项目&#xff0c;再进入Home页面展示对应项目的内容。 一、实现效果图预览 二、页面内容 功能1、项目列表展…...

用于牙科的多任务视频增强

Multi-task Video Enhancement for Dental Interventions 2022 miccai Abstract 微型照相机牢牢地固定在牙科手机上&#xff0c;这样牙医就可以持续地监测保守牙科手术的进展情况。但视频辅助牙科干预中的视频增强减轻了低光、噪音、模糊和相机握手等降低视觉舒适度的问题。…...

【Node.js]

一、概述 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 &#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c; 让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。 官网地…...

【Elasticsearch】腾讯云安装Elasticsearch

Elasticsearch 认识Elasticsearch安装Elasticsearch安装Kibana安装IK分词器分词器的作用是什么&#xff1f;IK分词器有几种模式&#xff1f;IK分词器如何拓展词条&#xff1f;如何停用词条&#xff1f; 认识Elasticsearch Elasticsearch的官方网站如下 Elasticsearch官网 Ela…...

【网络协议】ACL(访问控制列表)第一部分

概述 网络安全在网络中的重要性不言而喻。本文&#xff08;即第一部分&#xff09;将介绍ACL的基本概念以及标准ACL的配置。第二部分将重点讨论扩展ACL、其他相关概念以及ACL的故障排除。 文章目录 概述ACL定义数据包过滤ACLACL配置指导原则配置ACL的三条规则ACL功能ACL工作原…...

2025.1.20——一、[RCTF2015]EasySQL1 二次注入|报错注入|代码审计

题目来源&#xff1a;buuctf [RCTF2015]EasySQL1 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;初步思路为二次注入&#xff0c;在页面进行操作 step 2&#xff1a;尝试二次注入 step 3&#xff1a;已知双引号类型的字符型注入&#xff0c;构造…...

Spring Boot 整合 Knife4j:打造更优雅的 API 文档

在现代 Web 应用开发中&#xff0c;API 文档的重要性不言而喻。清晰、准确、易用的 API 文档不仅可以方便开发者理解和使用 API&#xff0c;还能提高团队协作效率。Knife4j 是一个基于 Swagger 的增强型 API 文档工具&#xff0c;它可以为 Spring Boot 项目生成美观、易于交互的…...

Kafka 源码分析(一) 日志段

首先我们的 kafka 的消息本身是存储在日志段中的, 对应的源码是下面这段代码: class LogSegment private[log] (val log: FileRecords,val lazyOffsetIndex: LazyIndex[OffsetIndex],val lazyTimeIndex: LazyIndex[TimeIndex],val txnIndex: TransactionIndex,val baseOffset:…...

javaEE初阶————多线程初阶(2)

今天给大家带来第二期啦&#xff0c;保证给大家讲懂嗷&#xff1b; 1&#xff0c;线程状态 NEW安排了工作还未开始行动RUNNABLE可工作的&#xff0c;或者即将工作&#xff0c;正在工作BLOCKED排队等待WAITING排队等待其他事TIMED_WAITING排队等待其他事TERMINATED工作完成了 …...

Redis学习笔记1【数据类型和常用命令】

Redis学习笔记 基础语法 1.数据类型 String: 最基本的类型&#xff0c;可以存储任何数据&#xff0c;例如文本或数字。示例值为 hello world。Hash: 用于存储键值对&#xff0c;适合存储对象或结构体。示例值为 {"name": "Jack", "age": 21}。…...

JavaWeb项目——查询角色列表到页面中——转发模式

一、知识点 1、req.getRequestDispatch与resp.sendRedirect跳转方式的比较 一、实现原理 1、req.getRequestDispatcher&#xff1a; 属于服务器端跳转&#xff0c;在服务器内部将请求转发给另一个资源&#xff08;如另一个 Servlet 或 JSP 页面&#xff09;。调用 getReques…...

feign调用跳过HTTPS的SSL证书校验配置详解

一、问题抛出 如果不配置跳过SSL证书校验&#xff0c;当Feign客户端尝试连接到一个使用自签名证书的服务器时&#xff0c;可能会抛出类似以下的异常&#xff1a; javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building faile…...

今天也是记录小程序进展的一天(破晓时8)

嗨嗨嗨朋友们&#xff0c;今天又来记录一下小程序的进展啦&#xff01;真是太激动了&#xff0c;项目又迈出了重要的一步&#xff0c;231啦&#xff01;感觉每一步的努力都在积累&#xff0c;功能逐渐完善&#xff0c;离最终上线的目标越来越近了。大家一直支持着这个项目&…...

SQL-leetcode—1084. 销售分析 III

1084. 销售分析 III 表&#xff1a; Product --------------------- | Column Name | Type | --------------------- | product_id | int | | product_name | varchar | | unit_price | int | --------------------- product_id 是该表的主键&#xff08;具有唯一值的列&…...

Linux C\C++编程-文件位置指针与读写文件数据块

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客 《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 Linu…...

Flask简介与安装以及实现一个糕点店的简单流程

目录 1. Flask简介 1.1 Flask的核心特点 1.2 Flask的基本结构 1.3 Flask的常见用法 1.3.1 创建Flask应用 1.3.2 路由和视图函数 1.3.3 动态URL参数 1.3.4 使用模板 1.4 Flask的优点 1.5 总结 2. Flask 环境创建 2.1 创建虚拟环境 2.2 激活虚拟环境 1.3 安装Flask…...

【自动化测试】—— Appium使用保姆教程

目录 一. 连接手机 1. 授权 2. 调试 3. 获取参数 二. 启动APP 1. 启动Appium服务 2. 启动Appium Inspector 3. 配置Appium Inspector 三. 功能说明 1. 主菜单功能 2. 快照视图菜单 3. 元素视图菜单 四. 常见问题 1. appPackage有多个设备时 一. 连接手机 1. 授权 首先将手机的开…...

西门子【Library of General Functions (LGF) for SIMATIC S7-1200 / S7-1500】

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 通用函数库 (LGF) 扩展了 TIA Portal 中用于 PLC 编程的 STEP 7 指令&#xff08;数学函数、时间、计数器 等&#xff09;。该库可以不受限制地使用&#xff0c;并包含 FIFO 、搜索功能、矩阵计算、 astro 计…...

IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载

IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载 在 IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载&#xff0c;可以让你在不重启应用的情况下看到代码修改的效果。以下是详细的配置步骤&#xff1a; 添加 spring-boot-devtools 依赖 在 pom.xml 文件中添加 …...

Python----Python高级(正则表达式:语法规则,re库)

一、正则表达式 1.1、概念 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、 regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff0…...

通过Ukey或者OTP动态口令实现windows安全登录

通过 安当SLA&#xff08;System Login Agent&#xff09;实现Windows安全登录认证&#xff0c;是一种基于双因素认证&#xff08;2FA&#xff09;的解决方案&#xff0c;旨在提升 Windows 系统的登录安全性。以下是详细的实现方法和步骤&#xff1a; 1. 安当SLA的核心功能 安…...

Node.js接收文件分片数据并进行合并处理

前言&#xff1a;上一篇文章讲了如何进行文件的分片&#xff1a;Vue3使用多线程处理文件分片任务&#xff0c;那么本篇文章主要看一下后端怎么接收前端上传来的分片并进行合并处理。 目录&#xff1a; 一、文件结构二、主要依赖1. express2. multer3. fs (文件系统模块)4. pat…...

Lsky-Pro在线图片搭建教程(Docker部署方式)

Lsky Pro+ 是一个使用 PHP 语言,采用 Laravel 框架开发的一款 Web 图片管理程序,中文名:兰空图床。如果你需要一个在线图床程序,那么这个开源项目可以帮助到你,部署流程非常简单。本章教程记录如何部署Lsky-Pro。 一、拉取镜像 docker pull halcyonazure/lsky-pro-docke…...

“深入浅出”系列之算法篇:(2)openCV、openMV、openGL

OpenCV是一个的跨平台计算机视觉库,可以运行在Linux囚、Windows 和Mac OS操作系统上。它轻量级而且高效,由一系列 C函数和少量C类构成&#xff0c;同时也提供了Python 接口&#xff0c;实现了图像处理和计算机视觉方面的很多通用算法。 OpenMV是一个开源&#xff0c;低成本&am…...