自定义记录日志的注解

自定义记录日志的注解

建日志表

sql 复制代码
CREATE TABLE `system_log` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `method_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '方法名称',
  `url` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '路径',
  `request_params` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求参数',
  `response_result` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '响应结果',
  `cost_time` int NOT NULL COMMENT '执行时间(毫秒)',
  `message` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '说明',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

实现代码

定义注解

java 复制代码
package com.study.systemlog;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogAOP {

}

与表对应的实体类

java 复制代码
package com.study.systemlog;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;

@Data
@TableName("system_log")
public class SystemLog {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String methodName;

    private String url;

    private String requestParams;

    private String responseResult;

    private Long costTime;

    private String message;

    private Date createTime;

}

这里我用的是mybatisplus,如果这里的类名不是表名的驼峰命名,就需要加上 @TableName("system_log") 这个注解。

比如类名是 SystemLogDB,那么mybatisplus就会把这个类映射到表 system_log_d_b,而不是system_log

@TableId(value = "id", type = IdType.AUTO) 这个注解指定了ID字段是自增的。若没有这个注解,虽然传入的对象id值为null,但mybatisplus会自动给id分配一个随机值,然后写入数据库。这样就不能依靠数据库实现ID自增。

Mapper 和 Service

java 复制代码
package com.study.systemlog;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SystemLogMapper extends BaseMapper<SystemLog> {}
java 复制代码
package com.study.systemlog;

public interface SystemLogService {

    int insertSystemLog(SystemLog systemLog);

}
java 复制代码
package com.study.systemlog;

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;

@Service
public class SystemLogServiceImpl implements SystemLogService {

    @Resource
    private SystemLogMapper systemLogMapper;

    @Override
    public int insertSystemLog(SystemLog systemLog) {
        systemLog.setCreateTime(new Date());
        return systemLogMapper.insert(systemLog);
    }

}

实现自定义注解

java 复制代码
package com.study.systemlog;

import com.alibaba.fastjson2.JSON;
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.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Service("systemLogAOPService")
public class SystemLogAOPService {

    @Resource
    private SystemLogService systemLogService;

    @Pointcut("@annotation(com.study.systemlog.SystemLogAOP)")
    public void LogPointCut() {}

    @Around("LogPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        SystemLog systemLog = new SystemLog();

        // 请求路径
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        systemLog.setUrl(request.getRequestURI());

        long start = System.currentTimeMillis();

        // 拦截的方法名称。当前正在执行的方法
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        systemLog.setMethodName(className + "." + methodName);

        // 拦截的放参数类型
        MethodSignature msig = (MethodSignature)point.getSignature();

        // 拦截的参数
        String[] parameterNames = msig.getParameterNames();
        Object[] args = point.getArgs();
        Map<String, Object> params = generateParams(parameterNames, args);
        systemLog.setRequestParams(JSON.toJSONString(params));

        try {
            // 获得被拦截的方法
            point.getTarget().getClass().getMethod(methodName, msig.getMethod().getParameterTypes());
        } catch (NoSuchMethodException | SecurityException e1) {
            log.error("获取方法异常:" + e1.getMessage());
        }

        // 执行方法返回对象
        Object object = point.proceed();
        try {
            systemLog.setResponseResult(JSON.toJSONString(object));
            systemLog.setCostTime(System.currentTimeMillis() - start);
            systemLog.setMessage("执行成功!");
        } catch (Throwable e) {
            systemLog.setCostTime(System.currentTimeMillis() - start);
            systemLog.setMessage("执行失败!" + e.getMessage());
            log.error("执行方法[" + methodName + "]异常!" + e.getMessage());
        }
        systemLogService.insertSystemLog(systemLog);
        return object;
    }

    private Map<String,Object> generateParams(String[] paramNames, Object[] values){
        Map<String,Object> rs = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            rs.put(paramNames[i],values[i]);
        }
        return rs;
    }

}

需要导入的依赖

java 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>${spring-boot.version}</version>
</dependency>

为了方便,所有类都在同一层目录,也就是同一个包。

使用注解

将注解标在需要记录日志的方法上,就能实现日志功能。

java 复制代码
@SystemLogAOP
@GetMapping("/{id}")
public AjaxResult get(@PathVariable Long id) {
    return studentService.selectStudentById(id);
}

实现效果

相关推荐
MacroZheng20 分钟前
完美替代 Navicat!这款内置 AI 的数据库工具,太香了!
java·后端·mysql
SamDeepThinking1 小时前
从REST到gRPC,一个API选型的思考框架
java·后端·程序员
Zane19941 小时前
接口都能写默认实现了,为什么还需要抽象类
java·后端
未秃头的程序猿1 小时前
读Spring AI源码的方法——不是硬啃,是带着问题去翻
java·后端·spring
用户3126874877201 小时前
别再 Thread.sleep 硬等了!并发工具类到底怎么选?
java
Java内核笔记1 小时前
万字长文剖析 Spring Boot 4 自动配置机制源码:从 @EnableAutoConfiguration 到条件装配
java·后端
evans在进步1 小时前
Spring MVC 核心机制详解:全局异常处理、请求跳转与数据绑定
java·spring·mvc
weixin_BYSJ19871 小时前
springboot小区物业管理小程序---附源码45555
java·javascript·spring boot·python·django·flask·php
coder_lorraine2 小时前
Halo 2.x 回收站文章无法删除?一篇文章教你彻底解决
java·运维
索隆zoro2 小时前
Army 进阶:DML 操作体系与 Spring AI 集成
java