Java 記錄操作日志|對象修改細節


背景描述

  由於業務涉及收入敏感信息,需記錄數據變更前的內容變更后的內容,但是不能為完成任務而硬編碼,要適用於不同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在此祝各位生活愉快!工作順利!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM