背景描述

  由于业务涉及收入敏感信息,需记录数据变更前的内容变更后的内容,但是不能为完成任务而硬编码,要适用于不同bean。针对这种情况,本文使用泛型、反射和基于AOP的自定义注解技术来完成,对对象属性的描述通过自定义注解来完成,读取里面的属性进而记录修改历史。

需求分析

  利用泛型、反射和自定义注解技术,分别比较修改前后两个Bean实例的、所有添加了自定义注解的成员变量,当值不一致时,记录变量名称和修改前后的值。 这种方法适用于处理不同的bean,可以达到一次编码,多处复用的效果。

  在对象中,需要比对是否变化的属性上加上自定义注解@PropertyMsg,value设置为属性的中文描述。工具类定义如下:

import com.swagger.demo.bean.ChangePropertyMsg;
import com.swagger.demo.bean.PropertyMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils; import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; /**
*
* @param <T>
*/
@Slf4j
public class BeanChangeUtil<T> { public ChangePropertyMsg contrastObj(Object oldBean, Object newBean) {
// 转换为传入的泛型T
T oldPojo = (T) oldBean;
// 通过反射获取类型及字段属性
Field[] fields = oldPojo.getClass().getDeclaredFields();
return jdk8OrAfter(Arrays.asList(fields), oldPojo, (T) newBean);
}
// lambda表达式,表达式内部的变量都是final修饰,需要传入final类型的数组
private ChangePropertyMsg jdk8OrAfter(List<Field> fields, T oldBean, T newBean) {
ChangePropertyMsg cf = new ChangePropertyMsg();
// 创建字符串拼接对象
StringBuilder str = new StringBuilder();
List<String> fieldList = new ArrayList<>();
// 属性改变个数
final int[] i = {1};
fields.forEach(field -> {
field.setAccessible(true);
if (field.isAnnotationPresent(PropertyMsg.class)) {
try {
// 获取属性值
Object newValue = field.get(newBean);
Object oldValue = field.get(oldBean);
if (ObjectUtils.notEqual(oldValue, newValue)) {
fieldList.add(field.getName());
str.append(i[0] + "、" + field.getAnnotation(PropertyMsg.class).propertyName() + ":")
.append("修改前->" + oldValue + ",修改后->" + newValue + "\n");
i[0]++;
}
} catch (Exception e) {
log.error("比对Bean属性是否变化失败,", e);
}
}
});
cf.setChangeMsg(str.toString());
cf.setProperties(fieldList);
return cf;
}
}

新bean一般由前端传过来,而旧bean需要去数据库查询。自定义注解@PropertyMsg如下所示:

import java.lang.annotation.*;

/**
* 属性信息注解,仅仅可以用于域声明
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PropertyMsg {
/**
* 提示语,用于标记哪个字段发生变更
* 更新时间:23年4月
* @return 提示语
*/
String propertyName() default ""; }

  下面创建一个User case:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; @Slf4j
public class TestChange { public static void main(String[] args) {
User u1 = new User(30L, "Wiener", 27);
User u2 = new User(30L, "楼兰胡杨", 20);
BeanChangeUtil<User> t = new BeanChangeUtil<>();
ChangePropertyMsg cfs = t.contrastObj(u1, u2);
if (StringUtils.isBlank(cfs.getChangeMsg())) {
log.info("未有改变");
} else {
// 属性发生变化,增加业务主键
cfs.setBizNum(u2.getId().toString());
log.info("属性变化:{}", cfs);
}
} }
-- ----------------------------------- import lombok.Getter;
import lombok.Setter;
import lombok.ToString; import java.io.Serializable; @Getter
@Setter
@ToString
public class User implements Serializable {
//实现serializable接口
private static final long serialVersionUID = -2241172936329900646L; private Long id;
@PropertyMsg(propertyName = "用户姓名")
private String userName;
private String msg;
@PropertyMsg(propertyName = "年龄")
private Integer age; /**
* 无参构造器
*/
public User() {
} public User(Long id, String userName, Integer age) {
this.id = id;
this.userName = userName;
this.age = age;
}

其中,ChangePropertyMsg用于记录属性变更结果,加到需要记录变化的字段,此注解代码如下:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString; import java.util.List; @Getter
@Setter
@ToString
public class ChangePropertyMsg { /**
* 业务主键
*/
private String bizNum;
/**
* 变更信息
*/
private String changeMsg;
/**
* 变更属性集合
*/
private List<String> properties;
}

  执行测试用例中的main函数,在控制台输出如下信息:

20:52:10.443 [main] INFO com.swagger.demo.bean.TestChange - 属性变化:ChangePropertyMsg(changeMsg=1、用户姓名:修改前->Wiener,修改后->楼兰胡杨
2、年龄:修改前->27,修改后->20
, properties=[userName, age])

和预期的输入结果一致。

  以上就是这篇文章的全部内容了,希望本文对道友的学习或者工作能带来一定的帮助,如有疑问请留言交流。Wiener在此祝各位生活愉快!工作顺利!

Java 记录操作日志|对象修改细节的更多相关文章

  1. Appfuse:记录操作日志

    appfuse的数据维护操作都发生在***form页面,与之对应的是***FormController,在Controller中处理数据的操作是onSubmit方法,既然所有的操作都通过onSubmi ...

  2. 使用SpringBoot AOP 记录操作日志、异常日志

    平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能:我们在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找到,而且也不能对发 ...

  3. MVC 记录操作日志与过滤特殊字符

    最近进行的MVC系统需要用到记录操作日志和过滤特殊字符的功能,如果每个action中都调用记录日志的方法就太麻烦了,所以根据需要结合mvc的过滤机制 写了个特殊字符验证与记录操作日志的公用类: pub ...

  4. Tomcat会话超时时怎样记录操作日志,满足安全审计要求

    众所周知.在实际的Web应用程序中,会话管理一般都採用Web容器会话管理功能. 使用Tomcat做Webserver也是如此,并且从安全的角度考虑,尽量避免去更改和干预Web容器的会话管理功能. To ...

  5. Java多线程操作同一个对象,线程不安全

    Java多线程操作同一个对象 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱 代码: package multithreading; // Java多线程操作同一个对象 // 买火车票的 ...

  6. SpringBoot-AOP记录操作日志

    package com.meeno.inner.oa.extend.operaterecord.aop; import com.alibaba.fastjson.JSONArray; import c ...

  7. spring-boot-route(十七)使用aop记录操作日志

    在上一章内容中--使用logback管理日志,我们详细讲述了如何将日志生成文件进行存储.但是在实际开发中,使用文件存储日志用来快速查询问题并不是最方便的,一个优秀系统除了日志文件还需要将操作日志进行持 ...

  8. Django记录操作日志、LogEntry的使用

    LogEntry是在后台开发中经常用到的模块,它在admin是默认开启的. 可以使用LogEntry模块记录所有用户的操作记录.一方面可以用来监督,另一方面可以用来做回滚. 1. 使用LogEntry ...

  9. Spring aop 记录操作日志 Aspect

    前几天做系统日志记录的功能,一个操作调一次记录方法,每次还得去收集参数等等,太尼玛烦了.在程序员的世界里,当你的一个功能重复出现多次,就应该想想肯定有更简单的实现方法.于是果断搜索各种资料,终于搞定了 ...

  10. 自定义日志注解 + AOP实现记录操作日志

      需求:系统中经常需要记录员工的操作日志和用户的活动日志,简单的做法在每个需要的方法中进行日志保存操作, 但这样对业务代码入侵性太大,下面就结合AOP和自定义日志注解实现更方便的日志记录   首先看 ...

随机推荐

  1. .net 8 C# 集成 AWS Cognito SMS/Email 注册与登录

    本文主要分为三个部分: 1.描述 cognito 涉及的专业术语 以及 交互流程 2..net 集成的代码 3.感想 * 阅读提示 :鼠标悬停在 章节标题 上可见 文章目录 1. Cognito 概念 ...

  2. Mpmath库-学习笔记

    目录 mpmath库学习 1. Introduction 1.2 Basic usage of mpmath 1.3 输出格式化 1.4 输出的小数点位数 2. BASIC FEATURES 2.1 ...

  3. 【BUG】axios 长数字精度丢失问题

    问题原因 出现改问题是于javascript 整数范围问题 java 中 Long 类型 -2的63次方 - 2的63次方减去1 但是javascript整数范围确没有那么大,导致Long数字过大前端 ...

  4. php7安装redis6扩展

    1.下载 php-redis扩展下载地址: http://pecl.php.net/package/redis 具体下载版本以自己的PHP版本信息为准 linux下载命令 wget http://pe ...

  5. 出现TypeError: float() argument must be a string or a number, not _NoValueType(机器学习 Win11)

    博客地址:https://www.cnblogs.com/zylyehuo/ 如果出现以下报错 则说明是torch.numpy等库的版本不匹配 可以去以下网站寻找匹配的版本 https://mirro ...

  6. Ubuntu更换cuda版本,gcc,g++版本

    Ubuntu更换cuda版本,gcc,g++版本 更换cuda版本 这个比较简单 可以看到 /usr/local下面有一个软链接,更换到我们需要的版本即可,cuda对应版本安装可参考官网. 创建软连接 ...

  7. 火焰图(Flame Graph)使用指南

    火焰图(Flame Graph) 是一种可视化性能分析工具,可以帮助你快速定位 CPU.内存或 I/O 瓶颈.它看起来像火焰,因此得名. 火焰图能解决什么问题? CPU 占用高:找出哪些函数消耗了最多 ...

  8. IDEA强制注册登录版本号:IntelliJ IDEA 2021.2.2

    建议采用 IntelliJ IDEA 2021.2.2 版本进行  Evaluate for free  试用 IntelliJ IDEA 2021.3.3  以前的版本可以不用注册登录idea账户, ...

  9. Selenium+pytest 页面对象模型框架

    下载地址:https://gitee.com/xiaopo1998/web_ui_test.git Selenium 页面对象模型框架使用说明 本框架基于 Selenium WebDriver 实现了 ...

  10. python aiohttp异步协程实现同时执行多条请求

    我们在对多个链接进行处理的时候,往往是先请求一个链接获得数据后,再请求第二个. 中间在等待返回数据时候,存在一个空闲时间,脚本啥都没干. 用aiohttp异步协程的方法,创建多条任务发送请求(理论上不 ...