0

0

怎么使用SpringBoot+Aop记录用户操作日志

PHPz

PHPz

发布时间:2023-05-11 21:16:04

|

921人浏览过

|

来源于亿速云

转载

1、设计用户操作日志表: sys_oper_log

怎么使用springboot+aop记录用户操作日志

对应实体类为SysOperLog.java

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;

/**
 * 

* 操作日志记录 *

*/ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class SysOperLog implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty("主键Id") private Integer id; @ApiModelProperty("模块标题") private String title; @ApiModelProperty("参数") private String optParam; @ApiModelProperty("业务类型(0其它 1新增 2修改 3删除)") private Integer businessType; @ApiModelProperty("路径名称") private String uri; @ApiModelProperty("操作状态(0正常 1异常)") private Integer status; @ApiModelProperty("错误消息") private String errorMsg; @ApiModelProperty("操作时间") private Date operTime; }

2、引入依赖


    com.alibaba
    fastjson
    2.0.9


   org.springframework.boot
   spring-boot-starter-aop

3、自定义用户操作日志注解

MyLog.java

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {
    // 自定义模块名,eg:登录
    String title() default "";
    // 方法传入的参数
    String optParam() default "";
    // 操作类型,eg:INSERT, UPDATE...
    BusinessType businessType() default BusinessType.OTHER;  
}

BusinessType.java — 操作类型枚举类

讯飞听见会议
讯飞听见会议

科大讯飞推出的AI智能会议系统

下载
public enum BusinessType {
    // 其它
    OTHER,
    // 查找
    SELECT,
    // 新增
    INSERT,
    // 修改
    UPDATE,
    // 删除
    DELETE,
}

4、自定义用户操作日志切面

LogAspect.java

import com.alibaba.fastjson.JSONObject;
import iot.sixiang.license.entity.SysOperLog;
import iot.sixiang.license.handler.IotLicenseException;
import iot.sixiang.license.jwt.UserUtils;
import iot.sixiang.license.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class LogAspect {
    /**
     * 该Service及其实现类相关代码请自行实现,只是一个简单的插入数据库操作
     */
    @Autowired
    private SysOperLogService sysOperLogService;
    
	/**
     * @annotation(MyLog类的路径) 在idea中,右键自定义的MyLog类-> 点击Copy Reference
     */
    @Pointcut("@annotation(xxx.xxx.xxx.MyLog)")
    public void logPointCut() {
        log.info("------>配置织入点");
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        handleLog(joinPoint, null);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    private void handleLog(final JoinPoint joinPoint, final Exception e) {

        // 获得MyLog注解
        MyLog controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null) {
            return;
        }
        SysOperLog operLog = new SysOperLog();
        // 操作状态(0正常 1异常)
        operLog.setStatus(0);
        // 操作时间
        operLog.setOperTime(new Date());
        if (e != null) {
            operLog.setStatus(1);
            // IotLicenseException为本系统自定义的异常类,读者若要获取异常信息,请根据自身情况变通
            operLog.setErrorMsg(StringUtils.substring(((IotLicenseException) e).getMsg(), 0, 2000));
        }

        // UserUtils.getUri();获取方法上的路径 如:/login,本文实现方法如下:
        // 1、在拦截器中 String uri = request.getRequestURI();
        // 2、用ThreadLocal存放uri,UserUtils.setUri(uri);
        // 3、UserUtils.getUri();
        String uri = UserUtils.getUri();
        operLog.setUri(uri);
        // 处理注解上的参数
        getControllerMethodDescription(joinPoint, controllerLog, operLog);
        // 保存数据库
        sysOperLogService.addOperlog(operLog.getTitle(), operLog.getBusinessType(), operLog.getUri(), operLog.getStatus(), operLog.getOptParam(), operLog.getErrorMsg(), operLog.getOperTime());
    }

    /**
     * 是否存在注解,如果存在就获取,不存在则返回null
     * @param joinPoint
     * @return
     */
    private MyLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(MyLog.class);
        }
        return null;
    }

    /**
     * 获取Controller层上MyLog注解中对方法的描述信息
     * @param joinPoint 切点
     * @param myLog 自定义的注解
     * @param operLog 操作日志实体类
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, MyLog myLog, SysOperLog operLog) {
        // 设置业务类型(0其它 1新增 2修改 3删除)
        operLog.setBusinessType(myLog.businessType().ordinal());
        // 设置模块标题,eg:登录
        operLog.setTitle(myLog.title());
        // 对方法上的参数进行处理,处理完:userName=xxx,password=xxx
        String optParam = getAnnotationValue(joinPoint, myLog.optParam());
        operLog.setOptParam(optParam);

    }

    /**
     * 对方法上的参数进行处理
     * @param joinPoint
     * @param name
     * @return
     */
    private String getAnnotationValue(JoinPoint joinPoint, String name) {
        String paramName = name;
        // 获取方法中所有的参数
        Map params = getParams(joinPoint);
        // 参数是否是动态的:#{paramName}
        if (paramName.matches("^#\\{\\D*\\}")) {
            // 获取参数名,去掉#{ }
            paramName = paramName.replace("#{", "").replace("}", "");
            // 是否是复杂的参数类型:对象.参数名
            if (paramName.contains(".")) {
                String[] split = paramName.split("\\.");
                // 获取方法中对象的内容
                Object object = getValue(params, split[0]);
                // 转换为JsonObject
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(object);
                // 获取值
                Object o = jsonObject.get(split[1]);
                return String.valueOf(o);
            } else {// 简单的动态参数直接返回
                StringBuilder str = new StringBuilder();
                String[] paraNames = paramName.split(",");
                for (String paraName : paraNames) {
                    
                    String val = String.valueOf(getValue(params, paraName));
                    // 组装成 userName=xxx,password=xxx,
                    str.append(paraName).append("=").append(val).append(",");
                }
                // 去掉末尾的,
                if (str.toString().endsWith(",")) {
                    String substring = str.substring(0, str.length() - 1);
                    return substring;
                } else {
                    return str.toString();
                }
            }
        }
        // 非动态参数直接返回
        return name;
    }

    /**
     * 获取方法上的所有参数,返回Map类型, eg: 键:"userName",值:xxx  键:"password",值:xxx
    * @param joinPoint
     * @return
     */
    public Map getParams(JoinPoint joinPoint) {
        Map params = new HashMap<>(8);
        // 通过切点获取方法所有参数值["zhangsan", "123456"]
        Object[] args = joinPoint.getArgs();
        // 通过切点获取方法所有参数名 eg:["userName", "password"]
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] names = signature.getParameterNames();
        for (int i = 0; i < args.length; i++) {
            params.put(names[i], args[i]);
        }
        return params;
    }

    /**
     * 从map中获取键为paramName的值,不存在放回null
     * @param map
     * @param paramName
     * @return
     */
    private Object getValue(Map map, String paramName) {
        for (Map.Entry entry : map.entrySet()) {
            if (entry.getKey().equals(paramName)) {
                return entry.getValue();
            }
        }
        return null;
    }
}

5、MyLog注解的使用

@GetMapping("login")
@MyLog(title = "登录", optParam = "#{userName},#{password}", businessType = BusinessType.OTHER)
public DataResult login(@RequestParam("userName") String userName, @RequestParam("password") String password) {
 ...
}

6、最终效果

怎么使用SpringBoot+Aop记录用户操作日志

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

801

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

722

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

727

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

395

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

445

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

428

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16860

2023.08.03

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Redis6入门到精通超详细教程
Redis6入门到精通超详细教程

共47课时 | 5.1万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.4万人学习

YMP在线手册
YMP在线手册

共64课时 | 32.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号