目录
案例
- 将案例中的增删改接口的操作日志记录到数据表中
操作日志
- 日志信息包括:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时的参数、返回值、犯法运行时长。
思路分析
- 需要对于所有业务类中的增删改查方法统一添加功能,使用AOP技术最方便,@Around环绕通知
- 由于增删改方法名命名不规律,可以自定义@Log注解完成目标方法的匹配
步骤
准备
- 引入AOP的起步依赖
- 导入资料中准备好的数据表结构,并引入对应的实体类
- 数据结构表
-
-- 操作日志表 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 '操作日志表';
-
-
实体类(用于封装操作日志信息)
-
package com.example.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; //操作耗时 }
-
- 数据结构表
编码
- 自定义注解@Log
-
package com.example.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) // todo 设置该疏解的生效时间 @Target(ElementType.METHOD) // todo 设置该注解可以作用在方法上 public @interface Log { }
-
- 定义切面类,完成记录操作日志的逻辑
-
package com.example.tlias.AOP; import com.alibaba.fastjson.JSONObject; import com.example.tlias.mapper.OperateLogMapper; import com.example.tlias.pojo.OperateLog; import com.example.tlias.utils.JwtUtils; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; 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 java.time.LocalDateTime; import java.util.Arrays; @Component @Aspect // todo 表名当前类是一个切面类 @Slf4j public class LogAspect { @Autowired private OperateLogMapper operateLogMapper; // todo 注入当前请求的请求对象 @Autowired private HttpServletRequest request; // todo 定义通知方法 @Around("@annotation(com.example.tlias.Anno.Log)") // todo 使用注解的方法设置切入点 public Object LogAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // todo 获取操作人的id--即当前登录员工的id // 获取请求头中的JWT令牌,解析令牌就可获取登录员工的ID String jwt = request.getHeader("token"); // 使用之前编写好的解析JWT令牌的工具类来解析获取到的JWT令牌 Claims claims = JwtUtils.parseJWT(jwt); Integer OperateUserID = (Integer) claims.get("id"); // todo 获取操作类名 String ClassName = proceedingJoinPoint.getTarget().getClass().getName(); // todo 获取操作时间 LocalDateTime OperateTime = LocalDateTime.now(); // todo 获取操方法名 String MethodName = proceedingJoinPoint.getSignature().getName(); // todo 获取操作方法参数 Object[] args = proceedingJoinPoint.getArgs(); // 将数组类型数据转换为字符串类型 String MethodParams = Arrays.toString(args); // todo 获取操作方法运行开始时间 // 先获取开始时间,方法运行完成之后再获取结束时间 Long start = System.currentTimeMillis(); // todo 获取操作方法返回值 // 调用原始目标方法运行 Object result = proceedingJoinPoint.proceed(); // 将对象专为JSON格式数据的字符串,仍然使用工具包JSONFast String returnValue = JSONObject.toJSONString(result); // todo 获取操作方法运行结束时间 Long end = System.currentTimeMillis(); // 计算方法运行耗时 Long coatTime = end - start; // todo 使用全参构造,完成数据的封装 OperateLog operateLog = new OperateLog(null, OperateUserID, OperateTime, ClassName, MethodName, MethodParams, returnValue, coatTime); // todo 向operateLog对象中封装相关信息 // operateLog.setClassName(ClassName); // operateLog.setCostTime(coatTime); // operateLog.setMethodName(MethodName); // operateLog.setMethodParams(MethodParams); // operateLog.setOperateTime(OperateTime); // operateLog.setOperateUser(OperateUserID); // operateLog.setReturnValue(returnValue); // operateLogMapper.insert(operateLog); // todo 完成日志数据的记录 operateLogMapper.insert(operateLog); log.info("AOP记录操作日志", operateLog); return result; } }
-
- 获取当前登录用户
- 获取request对象,从请求头中获取JWT令牌,解析令牌获取当前用户ID