[Java2023] Day13.11-综合案例-AOP实现日志记录

112 阅读2分钟

目标 :

将案例中的增删改相关接口操作的日志, 记录到数据表中 日志信息包含: 操作人, 操作时间, 执行方法的全类名, 执行方法名, 方法运行参数, 返回值, 方法运行时长

实现方式:

使用AOP, 并使用Around通知的方式

准备工作:

1. 在案例工程中引入AOP起步依赖
2. 导入资料中准备好数据表结构, 并创建实体类pojo

编码:

1. 自定义注解@Log
    2. 定义切面类, 完成记录操作日志的逻辑代码
    

相关准备

  • 1.起步依赖
 <!--AOP-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 2.日志表
-- 操作日志表
create table operate_log(
    id int unsigned primary key auto_increment comment 'ID',
    operate_user int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';

步骤

  • 1.创建日志实体类(模型对象)
package com.tlias.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Long costTime; //操作耗时
}
  • 2.创建日志操作类Mapper
package com.tlias.mapper;

import com.tlias.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperateLogMapper {

    //插入日志数据
    @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
            "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insert(OperateLog log);

}
  • 3.创建自定义注解类
package com.tlias.anno;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)

//  Log类是自定义注解类
public @interface Log {
}
  • 4.创建AOP切面类用来记录日志
package com.tlias.aop;

import com.alibaba.fastjson.JSONObject;
import com.tlias.mapper.OperateLogMapper;
import com.tlias.pojo.OperateLog;
import com.tlias.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Component
@Aspect
public class LogAspect {

   // http请求类, 用于获取请求参数
   @Autowired
   private HttpServletRequest request;

   // 日志记录类
   @Autowired
   private OperateLogMapper operateLogMapper;

   @Around("@annotation(com.tlias.anno.Log)")
   public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {

       // 调用目标方法运行,
       Object result = joinPoint.proceed();


       // 操作人: 从jwt中获取
       String jwt = request.getHeader("token");
       Claims claims = JwtUtils.parseJWT(jwt);
       Integer operateUser = (Integer) claims.get("id");

       // 操作时间
       LocalDateTime operateTime = LocalDateTime.now();

       // 操作类名
       String className = joinPoint.getTarget().getClass().getName();

       // 方法名
       String methodName = joinPoint.getSignature().getName();

       // 操作方法参数
       Object[] args = joinPoint.getArgs();
       String methodParams = Arrays.toString(args);

       // 方法返回值
       long begin = System.currentTimeMillis();
       Object res = joinPoint.proceed();
       long end = System.currentTimeMillis();

       String returnValue = JSONObject.toJSONString(res); // 方法返回值

       // 方法耗时
       long costTime = end - begin;

       // 记录日志
       // 实例化操作日志, 然后获取操作日志的值
       OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);
       operateLogMapper.insert(operateLog);

       log.info("AOP操作记录日志: {}", operateLog);
       return result;
   }
}
  • 5.在目标控制器中的增删改方法中添加@Log注解, 然后AOP通过切入点表达式匹配到这些方法, 从而进行切入
package com.tlias.controller;

public class DeptController {
       
    // 省略部分代码  

    @DeleteMapping("/depts/{id}")
    @Log
    public Result delete(@PathVariable Integer id) throws Exception {
        log.info("根据ID删除部门", id);
        deptService.delete(id);
        return Result.success();
    }

    @PostMapping("/depts")
    @Log
    public Result add(@RequestBody Dept dept) {
        log.info("新增部门:{}", dept);
        deptService.add(dept);
        return Result.success();
    }

    @PutMapping("/depts")
    @Log
    public Result update(@RequestBody Dept dept)
    {
        deptService.update(dept);
        return Result.success();
    }

}