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

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

今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下。

需求分析

系统需要对用户的操作进行记录,方便未来溯源

首先想到的就是在每个方法中,去实现记录的逻辑,但是这样做肯定是不现实的,首先工作量大,其次违背了软件工程设计原则(开闭原则)

这种需求显然是对代码进行增强,首先想到的是使用 SpringBoot 提供的 AOP 结合注解的方式来实现

功能实现

1、 需要一张记录日志的 Log 表

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

导出的 sql 如下:

-- mcams.t_log definition

CREATE TABLE `t_log` (
  `log_id` int NOT NULL AUTO_INCREMENT COMMENT '日志编号',
  `user_id` int NOT NULL COMMENT '操作人id',
  `operation` varchar(128) NOT NULL COMMENT '用户操作',
  `method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作的方法',
  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '方法的参数',
  `ip` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户的ip',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '操作时间',
  `cost_time` int DEFAULT NULL COMMENT '花费时间',
  PRIMARY KEY (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=189 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2、我使用的是 Spring Boot 所以需要引入 spring aop 的 starter

        org.springframework.bootspring-boot-starter-aop

如果是使用 spring 框架的,引入 spring-aop 即可

3、Log 实体类

package com.xiaofengstu.mcams.web.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;

/**
 *

* *

* * @author fengzeng * @since 2022-05-21 */ @Getter @Setter @TableName("t_log") public class TLog implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "log_id", type = IdType.AUTO) private Integer logId; /** * 操作人id */ @TableField("user_id") private Integer userId; /** * 用户操作 */ @TableField("operation") private String operation; @TableField("method") private String method; @TableField("params") private String params; @TableField("ip") private String ip; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField("create_time") private LocalDateTime createTime; @TableField("cost_time") private Long costTime; }

需要 lombok 插件(@getter && @setter 注解)

4、ILog 注解

package com.xiaofengstu.mcams.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author FengZeng
 * @Date 2022-05-21 00:48
 * @Description TODO
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ILog {
  String value() default "";
}

5、切面类 LogAspect

package com.xiaofengstu.mcams.aspect;

import com.xiaofengstu.mcams.annotation.ILog;
import com.xiaofengstu.mcams.util.ThreadLocalUtils;
import com.xiaofengstu.mcams.web.entity.TLog;
import com.xiaofengstu.mcams.web.service.TLogService;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @Author FengZeng
 * @Date 2022-05-21 00:42
 * @Description TODO
 */
@Aspect
@Component
@RequiredArgsConstructor
public class LogAspect {
  private final TLogService logService;

  @Pointcut("@annotation(com.xiaofengstu.mcams.annotation.ILog)")
  public void pointcut() {

  }

  @Around("pointcut()")
  public Object around(ProceedingJoinPoint point) {
    Object result = null;
    long beginTime = System.currentTimeMillis();

    try {
      result = point.proceed();
    } catch (Throwable e) {
      e.printStackTrace();
    }

    long costTime = System.currentTimeMillis() - beginTime;

    saveLog(point, costTime);

    return result;
  }

  private void saveLog(ProceedingJoinPoint point, long costTime) {
    // 通过 point 拿到方法签名
    MethodSignature methodSignature = (MethodSignature) point.getSignature();
    // 通过方法签名拿到被调用的方法
    Method method = methodSignature.getMethod();

    TLog log = new TLog();
    // 通过方法区获取方法上的 ILog 注解
    ILog logAnnotation = method.getAnnotation(ILog.class);
    if (logAnnotation != null) {
      log.setOperation(logAnnotation.value());
    }

    String className = point.getTarget().getClass().getName();
    String methodName = methodSignature.getName();
    log.setMethod(className + "." + methodName + "()");

    // 获取方法的参数
    Object[] args = point.getArgs();
    LocalVariableTableParameterNameDiscoverer l = new LocalVariableTableParameterNameDiscoverer();
    String[] parameterNames = l.getParameterNames(method);
    if (args != null && parameterNames != null) {
      StringBuilder param = new StringBuilder();
      for (int i = 0; i 

因为我使用的是 Mybatis-plus,所以 logService.save(log);是 mybatis-plus 原生的 save operation

这步其实就是把 log 插入到数据

6、使用

只需要在方法上加上 @ILog 注解,并设置它的 value 即可(value 就是描述当前 method 作用)

package com.xiaofengstu.mcams.web.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xiaofengstu.mcams.annotation.ILog;
import com.xiaofengstu.mcams.dto.BasicResultDTO;
import com.xiaofengstu.mcams.enums.RespStatusEnum;
import com.xiaofengstu.mcams.web.entity.TDept;
import com.xiaofengstu.mcams.web.service.TDeptService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 *

* 前端控制器 *

* * @author fengzeng * @since 2022-05-07 */ @RestController @RequestMapping("/web/dept") @RequiredArgsConstructor public class TDeptController { private final TDeptService deptService; @ILog("获取部门列表") @GetMapping("/list") public BasicResultDTO> getDeptListByCampId(@RequestParam("campusId") Integer campusId) { return new BasicResultDTO(RespStatusEnum.SUCCESS, deptService.list(new QueryWrapper().eq("campus_id", campusId))); } @ILog("通过角色获取部门列表") @GetMapping("/listByRole") public BasicResultDTO> getDeptListByRole() { return new BasicResultDTO(RespStatusEnum.SUCCESS, deptService.listByRole()); } }

数据库

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

总结

如果要对现有代码进行功能扩展,使用 AOP + 注解不妨为一种优雅的方式

对 AOP 不熟悉的小伙伴,可以深入了解一下,毕竟是 spring 最重要的特性之一。

发布者:糖太宗,转载请注明出处:https://www.qztxs.com/archives/science/technology/8303

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年5月22日 下午3:35
下一篇 2022年5月22日 下午4:04

相关推荐

  • 热力图生成算法及其具体实现

    目录 1. 概述 2. 详论 2.1. 数据准备 2.2. 准备绘制 2.3. 绘制热力范围 2.4. 绘制热力图 2.5. 配色方案 3. 问题 4. 参考 1. 概述 以前一直觉得热力图非常高大上,现在终于有机会研究并总结这个问题了。其实从图像处理的角度上来说,热力图生成算法并没有什么特别的,要得到非常漂亮的效果,数据以及配色方案的也很重要。这里就用Op...

    2022年5月22日
    5600
  • 老板问我,什么是关联规则推荐?

    工程架构方向的程序员,看到推荐/搜索/广告等和算法相关的技术,心中或多或少有一丝胆怯。但认真研究之后,发现其实没有这么难。 今天给大家介绍下推荐系统中的“关联规则推荐”,保证大伙弄懂。 画外音:可以看excel截图,或者看公式,大伙结合自己能够理解的程度自取。   一、概念 什么是关联规则(Association Rules)? 答:关联规则是数据挖掘中的概...

    2022年5月10日
    4800
  • insight洞察-漏洞生命周期管理使用

    简介 洞察是宜信安全部用来对公司内部系统所出现的安全漏洞进行线上全生命周期管理的漏洞管理平台。 项目地址 主要由3部分组成: 应用系统资产管理 漏洞生命周期管理 安全知识库管理 应用系统资产管理:对公司应用系统资产进行管理,包括系统名称、域名、重要级别、部门、负责人等。 漏洞生命周期管理:对公司应用系统产生的安全漏洞进行线上提交、通告、知悉、复测、分类、风险...

    2022年6月1日
    5700
  • Spring 源码(16)Spring Bean的创建过程(7)属性填充

    知识回顾 上一篇介绍了Spring中三级缓存的singletonObjects、earlySingletonObjects、singletonFactories,Spring在处理循环依赖时在实例化后属性填充前将一个lambda表达式放在了三级缓存中,后续在获取时进行了判断,如果不需要进行对象代理,那么直接返回对象Bean,然后将三级缓存中的对象删除,然后放...

    2022年5月22日
    3500
  • JumpServer堡垒机部署与运营

    前言 从应用安全,做成运维安全了,把最近做的东西总结一下。为啥需要堡垒机就不用多说了,服务器管理存在风险( 多云导致服务器分散、账号混乱定位不明、权限粗放资源滥用、误操作、审计不严取证困难、等保合规要求等),访问服务器存在安全隐患(直接对公网开放22和3389端口、办公网直连、服务器互联等),因此,部署实施堡垒机,利用堡垒机的4A能力(身份鉴别、授权控制、账...

    技术 2022年5月28日
    5300

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信