zoukankan      html  css  js  c++  java
  • java 记录对象前后修改的内容(工具类)

    有时候业务需要,需记录一条记录的修改历史,但是不能为完成任务而硬编码,不靠谱

    这种情况可以使用java反射来完成

    对对象属性的描述可以通过自定义注解来完成,读取里面的属性进而记录修改历史。

    在对象的属性上面加上注解,value设置为属性的中文描述

    工具了代码如下

    util类(BeanChangeUtil)

    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    public class BeanChangeUtil<T> {
        public String contrastObj(Object oldBean, Object newBean) {
            // 创建字符串拼接对象
            StringBuilder str = new StringBuilder();
            // 转换为传入的泛型T
            T pojo1 = (T) oldBean;
            T pojo2 = (T) newBean;
            // 通过反射获取类的Class对象
            Class clazz = pojo1.getClass();
            // 获取类型及字段属性
            Field[] fields = clazz.getDeclaredFields();
            return jdk8Before(fields, pojo1, pojo2, str,clazz);
    //        return jdk8OrAfter(fields, pojo1, pojo2, str,clazz);
        }
    
        // jdk8 普通循环方式
        public String jdk8Before(Field[] fields,T pojo1,T pojo2,StringBuilder str,Class clazz){
            int i = 1;
            try {
                for (Field field : fields) {
                    if(field.isAnnotationPresent(PropertyMsg.class)){
                        PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                        // 获取对应属性值
                        Method getMethod = pd.getReadMethod();
                        Object o1 = getMethod.invoke(pojo1);
                        Object o2 = getMethod.invoke(pojo2);
                        if (o1 == null || o2 == null) {
                            continue;
                        }
                        if (!o1.toString().equals(o2.toString())) {
                            str.append(i + "、" + field.getAnnotation(PropertyMsg.class).value() + ":" + "修改前=>" + o1 + ",修改后=>" + o2 + "
    ");
                            i++;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return str.toString();
        }
    
        // lambda表达式,表达式内部的变量都是final修饰,需要传入需要传入final类型的数组
        public String jdk8OrAfter(Field[] fields, T pojo1, T pojo2, StringBuilder str, Class clazz){
            final int[] i = {1};
            Arrays.asList(fields).forEach(f -> {
                if(f.isAnnotationPresent(PropertyMsg.class)){
                    try {
                        PropertyDescriptor pd = new PropertyDescriptor(f.getName(), clazz);
                        // 获取对应属性值
                        Method getMethod = pd.getReadMethod();
                        Object o1 = getMethod.invoke(pojo1);
                        Object o2 = getMethod.invoke(pojo2);
                        if (o1 == null || o2 == null) {
                            return;
                        }
                        if (!o1.toString().equals(o2.toString())) {
                            str.append(i[0] + "、" + f.getAnnotation(PropertyMsg.class).value() + ":" + "修改前=>" + o1 + "	修改后=>" + o2 + "
    ");
                            i[0]++;
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
            return str.toString();
        }
    }

    自定义注解(PropertyMsg)

    @Target

    表示该注解可以用于什么地方,可能的ElementType参数有:

      CONSTRUCTOR:构造器的声明

      FIELD:域声明(包括enum实例)

      LOCAL_VARIABLE:局部变量声明

      METHOD:方法声明

      PACKAGE:包声明

      PARAMETER:参数声明

      TYPE:类、接口(包括注解类型)或enum声明

    @Retention

    表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:

      SOURCE:注解将被编译器丢弃

      CLASS:注解在class文件中可用,但会被VM丢弃

      RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。

    @Document

    将注解包含在Javadoc中

    @Inherited

    允许子类继承父类中的注解

    import java.lang.annotation.*;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface PropertyMsg {
        String value();
    }

    使用方式test

    public class TestChange {
    
        public static void main(String[] args) {
            TestChange u1 = new TestChange("我是谁", "ok", 30,"刘德华");
            TestChange u2 = new TestChange("我在哪", "no", 20,"郭富城");
            BeanChangeUtil<TestChange> t = new BeanChangeUtil<>();
            String str = t.contrastObj(u1, u2);
            if (str.equals("")) {
                System.out.println("未有改变");
            } else {
                System.out.println(str);
            }
        }
    
        public TestChange() {
        }
    
        public TestChange(String about, String lock, Integer age, String name) {
            this.about = about;
            this.lock = lock;
            this.age = age;
            this.name = name;
        }
    
        @PropertyMsg("关于")
        private String about;
    
        private String lock;
    
        @PropertyMsg("年龄")
        private Integer age;
    
        @PropertyMsg("姓名")
        private String name;
    
        get... set... 省略
    }

    OK,到位

     https://github.com/chywx/JavaSE_chy

  • 相关阅读:
    模仿JavaAppArguments.java示例,编写一个程序,此程序从命令行接收多个数 字,求和之后输出结果,写出其的设计思想、程序流程图、源程序代码。
    大道至简第二章读后感
    大道至简第一章读后感
    md5实现
    jdk-动态代理
    使用反射复制对象
    java-二分查找法
    java-base64
    cxf框架使用(一)
    Freemarket学习笔记(一)
  • 原文地址:https://www.cnblogs.com/chywx/p/10634547.html
Copyright © 2011-2022 走看看