当前位置: 首页 > 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;具有唯一值的列&…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...