由于业务涉及收入敏感信息,需记录数据变更前的内容和变更后的内容,但是不能为完成任务而硬编码,要适用于不同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 { /** * 提示语,用于标记哪个字段发生变更 * * @return 提示语 */ String value(); }
下面创建一个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在此祝各位生活愉快!工作顺利!