Spring aop mybatis 审计日志数据操作前操作后对比记录

Posted wb54979

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring aop mybatis 审计日志数据操作前操作后对比记录相关的知识,希望对你有一定的参考价值。

源码下载地址 拖到最下面:

本文主要介绍如何使用mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录、在更新数据时准确记录更新字段

核心:mybatis插件(拦截器)、mybatis-Plus实体规范、数据对比 

mybatis插件

  mybatis插件实际上就是官方针对4层数据操作处理预留的拦截器,使用者可以根据不同的需求进行操作拦截并处理。这边笔者不做详细描述,详细介绍请到官网了解。这里不讲述背景,技术,只以代码的形式简单的阐述整个实现过程。

整个实现方式通过mybatis的拦截器+spring aop

maven仓库,省略其它仓库只贴mybatis plus

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

拦截器实现


import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;

import com.es.user.domain.aspect.DataOperateLogAspect;
import com.es.user.domain.threadlocah.DataOperateLogThreadLocal;
import com.es.user.util.log.OperationDataChange;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;
import java.sql.Statement;
import java.util.*;

/**
 * <p>
 * 数据更新拦截器
 * </p>
 *
 */
@Component
@Intercepts(@Signature(type = StatementHandler.class, method = "update", args = Statement.class))
public class DataOperateInterceptor extends AbstractSqlParserHandler implements Interceptor 

    private Logger logger = LoggerFactory.getLogger(DataOperateLogAspect.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Object intercept(Invocation invocation) throws Exception 

        // 判断是否需要记录日志
        if (DataOperateLogThreadLocal.DATA_CHANGES.get() == null) 
            return invocation.proceed();
        

        Statement statement;
        Object firstArg = invocation.getArgs()[0];

        if (Proxy.isProxyClass(firstArg.getClass())) 
            statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");
         else 
            statement = (Statement) firstArg;
        

        MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);

        try 
            statement = (Statement) stmtMetaObj.getValue("stmt.statement");
         catch (Exception e) 
        

        if (stmtMetaObj.hasGetter("delegate")) 
            try 
                statement = (Statement) stmtMetaObj.getValue("delegate");
             catch (Exception ignored) 
            
        

        String originalSql = statement.toString();
        originalSql = originalSql.replaceAll("[\\\\s]+", StringPool.SPACE);
        int index = indexOfSqlStart(originalSql);
        if (index > 0) 
            originalSql = originalSql.substring(index);
        

        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());

        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        if(mappedStatement.getSqlCommandType() != null)
            try
                // 获取执行Sql
                String sql = originalSql.replace("where", "WHERE");

                // 使用mybatis-plus 工具解析sql获取表名
                Collection<String> tables = new TableNameParser(sql).tables();
                if (CollectionUtils.isEmpty(tables)) 
                    return invocation.proceed();
                

                String tableName = tables.iterator().next();

                //更新数据
                if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) 

                    OperationDataChange change = new OperationDataChange();
                    change.setTableName(tableName);
                    change.setJdbcTemplate(jdbcTemplate);

                    // 设置sql用于执行完后查询新数据
                    String selectSql =  sql.substring(sql.lastIndexOf("WHERE") + 5);
                    // 同表对同条数据操作多次只进行一次对比
                    if (DataOperateLogThreadLocal.DATA_CHANGES.get().stream().anyMatch(c -> tableName.equals(c.getTableName())
                            && selectSql.equals(c.getWhereSql()))) 
                        return invocation.proceed();
                    

                    change.setWhereSql(selectSql);

                    // 获取请求时object
                    Object parameterObject = statementHandler.getParameterHandler().getParameterObject();
                    change.setTransferData(Arrays.asList(parameterObject));

                    String querySql = "select * from "+tableName+" where "+selectSql;
                    change.setQuerySql(querySql);

                    List<Map<String, Object>> maps = jdbcTemplate.queryForList(querySql);
                    //Object obj = JSONObject.parseObject(JSON.toJSONString(maps.get(0)),parameterObject.getClass());
                    change.setOldData(maps);
                    change.setEntityType(parameterObject.getClass());
                    DataOperateLogThreadLocal.DATA_CHANGES.get().add(change);
                
            catch (Exception e)

            
        
        return invocation.proceed();
    

    /**
     * 获取sql语句开头部分
     *
     * @param sql ignore
     * @return ignore
     */
    private int indexOfSqlStart(String sql) 
        String upperCaseSql = sql.toUpperCase();
        Set<Integer> set = new HashSet<>();
        set.add(upperCaseSql.indexOf("SELECT "));
        set.add(upperCaseSql.indexOf("UPDATE "));
        set.add(upperCaseSql.indexOf("INSERT "));
        set.add(upperCaseSql.indexOf("DELETE "));
        set.remove(-1);
        if (CollectionUtils.isEmpty(set)) 
            return -1;
        
        List<Integer> list = new ArrayList<>(set);
        list.sort(Comparator.naturalOrder());
        return list.get(0);
    

aop切面


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.es.user.annotation.DataOperateLog;
import com.es.user.domain.threadlocah.DataOperateLogThreadLocal;
import com.es.user.util.UUIDUtil;
import com.es.user.util.log.CompareObjUtil;
import com.es.user.util.log.OperationDataChange;
import com.es.user.util.log.OperationRecord;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;


/**
 * author : wb
 * File Name: DataOperateLogAspect
 * Package Name: com.es.user.domain.aspect
 * Date: 2022/5/24 15:17
 * Copyright (c) 2022,All Rights Reserved.
 */
@Aspect
@Component
public class DataOperateLogAspect 

    private Logger logger = LoggerFactory.getLogger(DataOperateLogAspect.class);

    @Autowired
    private DataOperateLogThreadLocal dataOperateLogThreadLocal;

    /**
     * <p>
     * 切面前执行
     * </p>
     *
     * @param dataLog dataLog
     * @return void
     */
    @Before("@annotation(dataLog)")
    public void beforeDataOperate(JoinPoint joinPoint, DataOperateLog dataLog) 
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String ClassName =  methodSignature.getDeclaringTypeName();
        DataOperateLogThreadLocal.THREADDATA_METHOD.set(ClassName+"#"+joinPoint.getSignature().getName());
        DataOperateLogThreadLocal.DATA_CHANGES.set(new LinkedList<>());
        DataOperateLogThreadLocal.JOIN_POINT.set(joinPoint);
        DataOperateLogThreadLocal.DATA_LOG.set(dataLog);
        DataOperateLogThreadLocal.THREADDATA_ID.set(UUID.randomUUID().toString().replace("-", "")+"-"+System.currentTimeMillis());
    

    /**
     * <p>
     * 切面后执行
     * </p>
     *
     * @param dataLog dataLog
     * @return void
     */
    @AfterReturning("@annotation(dataLog)")
    public void afterDataOperate(DataOperateLog dataLog) 
        try
            List<OperationDataChange> list = DataOperateLogThreadLocal.DATA_CHANGES.get();
            if (CollectionUtils.isEmpty(list)) 
                return;
            
            list.forEach(change -> 
                List<?> oldData = change.getOldData();
                if (CollectionUtils.isEmpty(oldData)) 
                    return;
                

                List<Map<String, Object>> maps = change.getJdbcTemplate().queryForList(change.getQuerySql());
                //Object obj = JSONObject.parseObject(JSON.toJSONString(maps.get(0)),change.getEntityType());
                change.setNewData(maps);
            );

            this.compareAndTransfer(list);
        catch (Exception e)

        
    

    /**
     * <p>
     * 对比保存
     * </p>
     *
     * @param list list
     * @return void
     */
    public void compareAndTransfer(List<OperationDataChange> list) 
        List<OperationRecord> saveData = new ArrayList<>();
        list.forEach(change -> 
            List<?> oldData = change.getOldData();
            List<?> newData = change.getNewData();
            // 更新前后数据量不对必定是删除(逻辑删除)不做处理
            if (newData == null) 
                return;
            
            if (oldData == null) 
                return;
            
            if (oldData.size() != newData.size()) 
                return;
            

            for (int i = 0; i < oldData.size(); i++) 
                try
                    String oldDataStr = JSON.toJSONString(oldData.get(i));
                    String newDataStr = JSON.toJSONString(newData.get(i));
                    String json = CompareObjUtil.campareJsonObject(oldDataStr,newDataStr);
                    OperationRecord operationRecord = new OperationRecord();
                    operationRecord.setChangeData(json);
                    operationRecord.setTransferData(JSON.toJSONString(change.getTransferData()));
                    operationRecord.setTableName(change.getTableName());
                    operationRecord.setBusinessProcessId(DataOperateLogThreadLocal.THREADDATA_ID.get().toString());
                    operationRecord.setId(UUIDUtil.randonUUID());
                    operationRecord.setOldObject(oldDataStr);
                    operationRecord.setNewObject(newDataStr);
                    operationRecord.setBusinessCallMethod(DataOperateLogThreadLocal.THREADDATA_METHOD.get().toString());
                    saveData.add(operationRecord);
                catch (Exception e)

                

            
        );

       logger.info("要保存的操作记录数据:",JSON.toJSONString(saveData));
       dataOperateLogThreadLocal.transfer();
    

DataOperateLogThreadLocal

import com.es.user.annotation.DataOperateLog;
import com.es.user.util.log.OperationDataChange;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * author : wb
 * File Name: DataOperateLogThreadLocal
 * Package Name: com.es.user.domain.loca
 * Date: 2022/5/20 16:26
 * Copyright (c) 2022,All Rights Reserved.
 */
@Service
public class DataOperateLogThreadLocal 

    /**
     * 注解
     */
    public static final ThreadLocal<DataOperateLog> DATA_LOG = new ThreadLocal<>();
    /**
     * 切点
     */
    public static final ThreadLocal<JoinPoint> JOIN_POINT = new ThreadLocal<>();
    /**
     * 数据变化
     */
    public static final ThreadLocal<List<OperationDataChange>> DATA_CHANGES = new ThreadLocal<>();

    /**
     * 当前线程操作生成的唯一ID
     */
    @SuppressWarnings("rawtypes")
    public static final ThreadLocal THREADDATA_ID = new ThreadLocal();

    /**
     * 当前操作的调用的方法
     */
    public static final ThreadLocal THREADDATA_METHOD = new ThreadLocal();


    /**
     * <p>
     * 调用
     * </p>
     *
     * @return void
     */
    public void transfer() 
        DATA_CHANGES.remove();
        DATA_LOG.remove();
        JOIN_POINT.remove();
        THREADDATA_ID.remove();
        THREADDATA_METHOD.remove();
    

切面注解

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataOperateLog 


    /**
     * 类型
     */
    int type() default -1;


    /**
     * 标签
     */
    String tag() default "";


    /**
     * 注释
     */
    String note() default "";

工具类


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.lang.reflect.Field;
import java.util.*;

/**
 * author : wb
 * File Name: CompareObjUtil
 * Package Name: com.es.user.util.log
 * Date: 2022/5/24 17:43
 * Copyright (c) 2022,All Rights Reserved.
 */
public class CompareObjUtil 

    /**
     * @description  比较两个对象的不同
     * @param
     * @return
     */
    public static List<Comparison> compareObj(Object beforeObj, Object afterObj) throws Exception
        List<Comparison> diffs = new ArrayList<>();

        if(beforeObj == null) 
            throw new RuntimeException("原对象不能为空");
        
        if(afterObj == null) 
            throw new RuntimeException("新对象不能为空");
        
        if(!beforeObj.getClass().isAssignableFrom(afterObj.getClass()))
            throw new RuntimeException("两个对象不相同,无法比较");
        

        //取出属性
        Field[] beforeFields = beforeObj.getClass().getDeclaredFields();
        Field[] afterFields = afterObj.getClass().getDeclaredFields();
        Field.setAccessible(beforeFields, true);
        Field.setAccessible(afterFields, true);

        //遍历取出差异值
        if(beforeFields != null && beforeFields.length > 0)
            for(int i=0; i<beforeFields.length; i++)
                Object beforeValue = beforeFields[i].get(beforeObj);
                Object afterValue = afterFields[i].get(afterObj);
                if((beforeValue != null && !"".equals(beforeValue) && !beforeValue.equals(afterValue)) || ((beforeValue == null || "".equals(beforeValue)) && afterValue != null))
                    Comparison comparison = new Comparison();
                    comparison.setField(beforeFields[i].getName());
                    comparison.setBefore(beforeValue);
                    comparison.setAfter(afterValue);
                    diffs.add(comparison);
                
            
        

        return diffs;
    

    /**
     * @description 比较两个json串的不同
     * @param
     * @return
     */
    public static String campareJsonObject(String oldJsonStr, String newJsonStr1) 
        //将字符串转换为json对象
        JSON oldJson = JSON.parseObject(oldJsonStr);
        JSON newJson = JSON.parseObject(newJsonStr1);
        //递归遍历json对象所有的key-value,将其封装成path:value格式进行比较
        Map<String, Object> oldMap = new LinkedHashMap<>();
        Map<String, Object> newMap = new LinkedHashMap<>();
        convertJsonToMap(oldJson, "", oldMap);
        convertJsonToMap(newJson, "", newMap);
        Map<String, Object> differenceMap = campareMap(oldMap, newMap);
        //将最终的比较结果把不相同的转换为json对象返回
        String jsonObject = convertMapToJson(differenceMap);
        return jsonObject;
    

    /**
     * 将json数据转换为map存储用于比较
     *
     * @param json
     * @param root
     * @param resultMap
     */
    private static void convertJsonToMap(Object json, String root, Map<String, Object> resultMap) 
        if (json instanceof JSONObject) 
            JSONObject jsonObject = ((JSONObject) json);
            Iterator iterator = jsonObject.keySet().iterator();
            while (iterator.hasNext()) 
                Object key = iterator.next();
                Object value = jsonObject.get(key);
                String newRoot = "".equals(root) ? key + "" : root + "." + key;
                if (value instanceof JSONObject || value instanceof JSONArray) 
                    convertJsonToMap(value, newRoot, resultMap);
                 else 
                    resultMap.put(newRoot, value);
                
            
         else if (json instanceof JSONArray) 
            JSONArray jsonArray = (JSONArray) json;
            for (int i = 0; i < jsonArray.size(); i++) 
                Object vaule = jsonArray.get(i);
                String newRoot = "".equals(root) ? "[" + i + "]" : root + ".[" + i + "]";
                if (vaule instanceof JSONObject || vaule instanceof JSONArray) 
                    convertJsonToMap(vaule, newRoot, resultMap);
                 else 
                    resultMap.put(newRoot, vaule);
                
            
        
    

    /**
     * 比较两个map,返回不同数据
     *
     * @param oldMap
     * @param newMap
     * @return
     */
    private static Map<String, Object> campareMap(Map<String, Object> oldMap, Map<String, Object> newMap) 
        //遍历newMap,将newMap的不同数据装进oldMap,同时删除oldMap中与newMap相同的数据
        campareNewToOld(oldMap, newMap);
        //將舊的有新的沒有的數據封裝數據結構存在舊的裡面
        campareOldToNew(oldMap);
        return oldMap;
    

    /**
     * 將舊的有新的沒有的數據封裝數據結構存在舊的裡面
     * @param oldMap
     * @return
     */
    private static void campareOldToNew(Map<String, Object> oldMap) 
        //统一oldMap中newMap不存在的数据的数据结构,便于解析
        for (Iterator<Map.Entry<String, Object>> it = oldMap.entrySet().iterator(); it.hasNext(); ) 
            Map.Entry<String, Object> item = it.next();
            String key = item.getKey();
            Object value = item.getValue();
            int lastIndex = key.lastIndexOf(".");
            if (!(value instanceof Map)) 
                Map<String, Object> differenceMap = new HashMap<>();
                differenceMap.put("oldValue", value);
                differenceMap.put("newValue", "");
                oldMap.put(key, differenceMap);
            
        
    

    /**
     * 將新的map與舊的比較,並將數據統一存在舊的裡面
     * @param oldMap
     * @param newMap
     */
    private static void campareNewToOld(Map<String, Object> oldMap, Map<String, Object> newMap) 
        for (Iterator<Map.Entry<String, Object>> it = newMap.entrySet().iterator(); it.hasNext(); ) 
            Map.Entry<String, Object> item = it.next();
            String key = item.getKey();
            Object newValue = item.getValue();
            Map<String, Object> differenceMap = new HashMap<>();
            int lastIndex = key.lastIndexOf(".");
            String lastPath = key.substring(lastIndex + 1).toLowerCase();
            if (oldMap.containsKey(key)) 
                Object oldValue = oldMap.get(key);
                if (newValue.equals(oldValue)) 
                    oldMap.remove(key);
                    continue;
                 else 
                    differenceMap.put("oldValue", oldValue);
                    differenceMap.put("newValue", newValue);
                    oldMap.put(key, differenceMap);
                
             else 
                differenceMap.put("oldValue", "");
                differenceMap.put("newValue", newValue);
                oldMap.put(key, differenceMap);
            
        
    

    /**
     * 将已经找出不同数据的map根据key的层级结构封装成json返回
     *
     * @param map
     * @return
     */
    private static String convertMapToJson(Map<String, Object> map) 
        JSONObject resultJSONObject = new JSONObject();
        for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext(); ) 
            Map.Entry<String, Object> item = it.next();
            String key = item.getKey();
            Object value = item.getValue();
            String[] paths = key.split("\\\\.");
            int i = 0;
            Object remarkObject = null;//用於深度標識對象
            int indexAll = paths.length - 1;
            while (i <= paths.length - 1) 
                String path = paths[i];
                if (i == 0) 
                    //初始化对象标识
                    if (resultJSONObject.containsKey(path)) 
                        remarkObject = resultJSONObject.get(path);
                     else 
                        if (indexAll > i) 
                            if (paths[i + 1].matches("\\\\[[0-9]+\\\\]")) 
                                remarkObject = new JSONArray();
                             else 
                                remarkObject = new JSONObject();
                            
                            resultJSONObject.put(path, remarkObject);
                         else 
                            resultJSONObject.put(path, value);
                        
                    
                    i++;
                    continue;
                
                if (path.matches("\\\\[[0-9]+\\\\]")) //匹配集合对象
                    int startIndex = path.lastIndexOf("[");
                    int endIndext = path.lastIndexOf("]");
                    int index = Integer.parseInt(path.substring(startIndex + 1, endIndext));
                    if (indexAll > i) 
                        if (paths[i + 1].matches("\\\\[[0-9]+\\\\]")) 
                            while (((JSONArray) remarkObject).size() <= index) 
                                if(((JSONArray) remarkObject).size() == index)
                                    ((JSONArray) remarkObject).add(index,new JSONArray());
                                else
                                    ((JSONArray) remarkObject).add(null);
                                
                            
                         else 
                            while(((JSONArray) remarkObject).size() <= index)
                                if(((JSONArray) remarkObject).size() == index)
                                    ((JSONArray) remarkObject).add(index,new JSONObject());
                                else
                                    ((JSONArray) remarkObject).add(null);
                                
                            
                        
                        remarkObject = ((JSONArray) remarkObject).get(index);
                     else 
                        while(((JSONArray) remarkObject).size() <= index)
                            if(((JSONArray) remarkObject).size() == index)
                                ((JSONArray) remarkObject).add(index, value);
                            else
                                ((JSONArray) remarkObject).add(null);
                            
                        
                    
                 else 
                    if (indexAll > i) 
                        if (paths[i + 1].matches("\\\\[[0-9]+\\\\]")) 
                            if(!((JSONObject) remarkObject).containsKey(path))
                                ((JSONObject) remarkObject).put(path, new JSONArray());
                            
                         else 
                            if(!((JSONObject) remarkObject).containsKey(path))
                                ((JSONObject) remarkObject).put(path, new JSONObject());
                            
                        
                        remarkObject = ((JSONObject) remarkObject).get(path);
                     else 
                        ((JSONObject) remarkObject).put(path, value);
                    
                
                i++;
            
        
        return JSON.toJSONString(resultJSONObject);
    



import java.io.Serializable;

/**
 * author : wb
 * File Name: Comparison
 * Package Name: com.es.user.util.log
 * Date: 2022/5/24 17:43
 * Copyright (c) 2022,All Rights Reserved.
 */
public class Comparison implements Serializable 
    //字段
    private String Field;
    //字段旧值
    private Object before;
    //字段新值
    private Object after;

    public String getField() 
        return Field;
    

    public void setField(String field) 
        Field = field;
    

    public Object getBefore() 
        return before;
    

    public void setBefore(Object before) 
        this.before = before;
    

    public Object getAfter() 
        return after;
    

    public void setAfter(Object after) 
        this.after = after;
    

import org.springframework.jdbc.core.JdbcTemplate;

import java.io.Serializable;
import java.util.List;

/**
 * author : wb
 * File Name: DataChange
 * Package Name: com.es.user.util.log
 * Date: 2022/5/24 15:14
 * Copyright (c) 2022,All Rights Reserved.
 */
public class OperationDataChange implements Serializable 

    /**
     * 当前数据变更的流程ID
     */
    private String dataId;

    /**
     * jdbctemplate
     */
    private JdbcTemplate jdbcTemplate;
    /**
     * sqlStatement
     */
    private String querySql;
    /**
     * 表名
     */
    private String tableName;
    /**
     * where 条件
     */
    private String whereSql;
    /**
     * 对应实体类
     */
    private Class<?> entityType;
    /**
     * 更新前数据
     */
    private List<?> oldData;
    /**
     * 更新后数据
     */
    private List<?> newData;

    /**
     * 传递的数据
     */
    private List<?> transferData;

    public List<?> getTransferData() 
        return transferData;
    

    public void setTransferData(List<?> transferData) 
        this.transferData = transferData;
    

    public String getDataId() 
        return dataId;
    

    public void setDataId(String dataId) 
        this.dataId = dataId;
    

    public JdbcTemplate getJdbcTemplate() 
        return jdbcTemplate;
    

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) 
        this.jdbcTemplate = jdbcTemplate;
    

    public String getQuerySql() 
        return querySql;
    

    public void setQuerySql(String querySql) 
        this.querySql = querySql;
    

    public String getTableName() 
        return tableName;
    

    public void setTableName(String tableName) 
        this.tableName = tableName;
    

    public String getWhereSql() 
        return whereSql;
    

    public void setWhereSql(String whereSql) 
        this.whereSql = whereSql;
    

    public Class<?> getEntityType() 
        return entityType;
    

    public void setEntityType(Class<?> entityType) 
        this.entityType = entityType;
    

    public List<?> getOldData() 
        return oldData;
    

    public void setOldData(List<?> oldData) 
        this.oldData = oldData;
    

    public List<?> getNewData() 
        return newData;
    

    public void setNewData(List<?> newData) 
        this.newData = newData;
    


import java.io.Serializable;

/**
 * author : wb
 * File Name: OperationRecords
 * Package Name: com.es.user.util.log
 * Date: 2022/5/24 18:52
 * Copyright (c) 2022,All Rights Reserved.
 */
public class OperationRecord implements Serializable 

    private Long id;

    /**
     * 操作业务流程ID
     */
    private String businessProcessId;

    /**
     * 变更之后数据发生的变更
     */
    private String changeData;

    /**
     * 传递的值
     */
    private String transferData;

    /**
     * 原数据值
     */
    private String oldObject;

    /**
     * 新数据值
     */
    private String newObject;

    /**
     * 操作的表明
     */
    private String tableName;

    /**
     * 业务调用的方法
     */
    private String businessCallMethod;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getChangeData() 
        return changeData;
    

    public void setChangeData(String changeData) 
        this.changeData = changeData;
    

    public String getTransferData() 
        return transferData;
    

    public void setTransferData(String transferData) 
        this.transferData = transferData;
    

    public String getOldObject() 
        return oldObject;
    

    public void setOldObject(String oldObject) 
        this.oldObject = oldObject;
    

    public String getNewObject() 
        return newObject;
    

    public void setNewObject(String newObject) 
        this.newObject = newObject;
    

    public String getBusinessProcessId() 
        return businessProcessId;
    

    public void setBusinessProcessId(String businessProcessId) 
        this.businessProcessId = businessProcessId;
    

    public String getTableName() 
        return tableName;
    

    public void setTableName(String tableName) 
        this.tableName = tableName;
    

    public String getBusinessCallMethod() 
        return businessCallMethod;
    

    public void setBusinessCallMethod(String businessCallMethod) 
        this.businessCallMethod = businessCallMethod;
    

调用实现

@Service
public class UserInfoBL 
   /**
     * 变更用户数据
     *
     * @param dto
     */
    @DataOperateLog
    public void updateSysUserInfo(UserInfoDTO dto) 
        userInfoMapper.updateSysUserInfo(dto);
    
import com.es.user.domain.bl.app.UserInfoBL;
import com.es.user.dto.app.UserInfoDTO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(value="dev")
public class UserTest

    @Autowired
    private UserInfoBL userInfoBL;

    @Test
    public void testUpdateUserInfo() 
        UserInfoDTO infoDTO = new UserInfoDTO();
        infoDTO.setUserId("8639115745654494218");
        infoDTO.setState(0);
        userInfoBL.updateSysUserInfo(infoDTO);
    

调用打印结果

[
    
        "businessCallMethod":"com.yijiupi.himalaya.easysale.user.domain.bl.app.UserInfoBL#updateSysUserInfo",
        "businessProcessId":"be5ec2405d64427fa38185422e2e9484-1653446567181",
        "changeData":"\\"last_update_time\\":\\"newValue\\":1653446571000,\\"oldValue\\":1648545800000,\\"state\\":\\"newValue\\":0,\\"oldValue\\":1",
        "id":5175759873968579967,
        "newObject":"\\"id\\":8639115745654494218,\\"user_name\\":\\"13871905563\\",\\"user_password\\":\\"e10adc3949ba59abbe56e057f20f883e\\",\\"mobile_no\\":\\"13871905563\\",\\"nick_name\\":\\"13871905563\\",\\"wx_app_id\\":\\"oBl7n5Z03UUGUVSJXlP--l8XsDG0\\",\\"state\\":0,\\"create_time\\":1636447507000,\\"last_update_time\\":1653446571000,\\"operator\\":\\"8639115745654494218\\",\\"user_type\\":5,\\"login_user_name\\":\\"9UBUDGZME\\",\\"data_source\\":4",
        "oldObject":"\\"id\\":8639115745654494218,\\"user_name\\":\\"13871905563\\",\\"user_password\\":\\"e10adc3949ba59abbe56e057f20f883e\\",\\"mobile_no\\":\\"13871905563\\",\\"nick_name\\":\\"13871905563\\",\\"wx_app_id\\":\\"oBl7n5Z03UUGUVSJXlP--l8XsDG0\\",\\"state\\":1,\\"create_time\\":1636447507000,\\"last_update_time\\":1648545800000,\\"operator\\":\\"8639115745654494218\\",\\"user_type\\":5,\\"login_user_name\\":\\"9UBUDGZME\\",\\"data_source\\":4",
        "tableName":"dealer_login_user",
        "transferData":"[\\"state\\":0,\\"userId\\":\\"8639115745654494218\\"]"
    
]

项目源码地址整合上述代码:wb54979 / easysale-framework · GitCode

以上是关于Spring aop mybatis 审计日志数据操作前操作后对比记录的主要内容,如果未能解决你的问题,请参考以下文章

spring aop mybatis puls 审计日志数据操作前操作后对比记录

我使用Spring AOP实现了用户操作日志功能

审计日志中的AOP

Spring Boot 入门:集成 AOP 进行日志管理

spring aop

Spring之AOP