当前位置:

首页 > 编程开发 > 怎么使用SpringBoot+Aop记录用户操作日志

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

1、设计用户操作日志表:sys_oper_log对应实体类为SysOperLog.javaimportcom.baomidou.mybatisplus.annotation.IdType;importcom.baomidou.mybatisplus.annotation.TableId;importio.swagger.annotations.ApiModelProperty;importlombok.Data;importlombok.EqualsAndHashCode;importlombok.exp

本文目录

    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 — 操作类型枚举类

    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记录用户操作日志

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    Java实现AOP日志记录配置与代码详解
    Java实现AOP日志记录配置与代码详解

    AOP日志记录通过将日志功能与业务逻辑解耦,使代码更清晰、维护更便捷。1.首先需引入SpringAOP和AspectJ依赖;2.定义切点(Pointcut)指定拦截方法;3.编写通知(Advice)处理日志逻辑,如@Before、@AfterReturning等;4.创建切面(Aspect)整合切点与通知,并标注@Aspect和@Component;5.启用AOP自动代理。敏感数据可通过脱敏、限制记录字段或调整日志级别来避免泄露。性能影响取决于日志复杂度,建议异步记录和优化操作。选择切点表达式时应结合方法

    Spring框架中 AOP(面向方面编程)是如何实现的?
    Spring框架中 AOP(面向方面编程)是如何实现的?

    SpringAOP基于Java动态代理来实现面向方面编程,允许在不修改原始代码情况下,在方法执行前后插入附加逻辑。具体步骤如下:创建代理对象,使用Proxy.newProxyInstance()方法,提供类加载器、接口和调用处理器。调用处理器的invoke()方法,获取目标对象、拦截器链,并依次调用拦截器invoke()。最后,如果没有异常,调用目标对象的方法。

    PHP中的面向切面编程(AOP)初探
    PHP中的面向切面编程(AOP)初探

    随着软件开发的不断发展,越来越多的编程语言和技术层出不穷。其中,PHP作为一门广泛应用于Web开发的脚本语言,在不断发展和革新的过程中,也涌现出了许多重要的概念和技术,如面向对象编程(OOP)、设计模式等,而今天我们要探讨的是PHP中的另一种编程方式——面向切面编程(AOP)。什么是AOP?AOP,全称是Aspect-OrientedProgramming

    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    探索Spring Boot架构:深入解析框架内部机制
    探索Spring Boot架构:深入解析框架内部机制

    SpringBoot的架构可以分为以下几个主要组件:Spring核心模块:springBoot的核心模块提供了Spring框架的基本功能,包括依赖注入、aop、事务管理等。这些模块是Spring应用程序的基础,为SpringBoot的开发提供了坚实的基础。SpringBootStarter:SpringBootStarter是一个预先配置好的依赖项集合,它可以帮助开发者快速构建Spring应用程序。SpringBootStarter提供了各种各样的依赖项,涵盖了各种常用的功能,如web开发、数据访问、安全

    解析SpringBoot和SpringMVC之间的异同
    解析SpringBoot和SpringMVC之间的异同

    解析SpringBoot和SpringMVC之间的异同SpringBoot和SpringMVC是Java领域中非常重要的开发框架。虽然它们都属于Spring框架的一部分,但是在使用和功能上有一些明显的区别。本文将对SpringBoot和SpringMVC进行比较,解析它们之间的异同。首先,让我们来了解一下SpringBoot。SpringBo

    深入了解Spring中AOP的典型应用方式
    深入了解Spring中AOP的典型应用方式

    探索Spring中AOP的常见应用方式概述:随着软件开发的不断发展,业务逻辑的复杂性也日益增加。为了提高代码的可维护性和重用性,以及实现切面关注点的分离,面向切面编程(AOP)的概念被引入到软件开发中。Spring框架是Java开发中广泛应用的框架之一,也提供了强大的AOP支持。本文将探索Spring中AOP的常见应用方式,并提供具体的代码示例。一、前置通知

    解析Spring AOP的常见应用场景
    解析Spring AOP的常见应用场景

    Spring中AOP的常见应用方式解析引言:在软件开发过程中,面向切面编程(AOP)是一种很重要的技术,它能够通过在程序运行期间动态地将特定的代码片段织入到目标方法中,提供额外的功能和扩展。而Spring作为一个强大的开发框架,提供了丰富的AOP支持,本文将详细介绍Spring中AOP的常见应用方式,包括声明式和编程式两种方式,并提供具体的代码示例。一、声明

    探索SpringCloud和SpringBoot在微服务领域的不同应用方式
    探索SpringCloud和SpringBoot在微服务领域的不同应用方式

    近年来,随着云计算和分布式架构的兴起,微服务架构的应用越来越广泛。而SpringCloud和SpringBoot作为Java开发中的两个重要框架,对于微服务的实现起到了重要的作用。然而,很多人对于它们在微服务领域的不同应用方式还存在一定的疑惑。本文将从不同的角度探索SpringCloud和SpringBoot在微服务中的应用方式。首先,我们来了解一下Spri

    SpringBoot和SpringMVC有何区别?
    SpringBoot和SpringMVC有何区别?

    SpringBoot与SpringMVC的不同之处在哪里?SpringBoot和SpringMVC是两个非常流行的Java开发框架,用于构建Web应用程序。尽管它们经常分别被使用,但它们之间的不同之处也是很明显的。首先,SpringBoot可以被看作是一个Spring框架的扩展或者增强版。它旨在简化Spring应用程序的初始化和配置过程,以帮助开发人

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。