SpringBoot使用在控制层切面注解配置的方式将日志存储在mysql

时间:2022-10-26 09:55:50

????程序员小王的博客:程序员小王的博客
????CSDN地址:程序员小王java
???? 欢迎点赞 ???? 收藏 ⭐留言 ????
???? 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕
????java自学的学习路线:java自学的学习路线

一、前言

我们写完一个项目,运维时,如果出现了bug,我们需要查看控制台的日志,但是那个日志无关方法太多,查找不是很方便,还有就是一个项目上线之后,我们需要记录谁操作了那些功能,以防出现矛盾知道是谁点了这个功能造成的问题,由谁来负责,为了解决这两个问题,我在SpringBoot项目中使用了对控制层切面+注解的方法来实现将日志存储在数据库里面

二、实现详细源码

1、相关依赖

  • springboot项目的依赖需要,还需要一个aop切面的依赖,mybatis的依赖
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
        <dependencies>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
           <!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>     
       </dependencies>

2、配置文件

在application.properties文件里加这样一条配置

server.port: 8080
spring.aop.auto=true

3、我们首先需要准备mysql表

DROP TABLE IF EXISTS `syslog`;
CREATE TABLE `syslog`  (
  `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
  `operation` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作',
  `method` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '方法名',
  `createDate` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '日志' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

SpringBoot使用在控制层切面注解配置的方式将日志存储在mysql

4、创建数据库表之后就需要写实体类了

package com.sgsg.verification.entity;
/**
 * @author 王恒杰
 * @date 2022/10/25 14:54
 * @Description:
 */
import java.io.Serializable;
public class Syslog implements Serializable {
    private String id;  //我用的UUIT

    private String username; //用户名

    private String operation; //操作

    private String method; //方法名

    private String createDate; //操作时间,这里可以使用Date来实现。我写的有个工具类。用的String接收

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getOperation() {
        return operation;
    }

    public void setOperation(String operation) {
        this.operation = operation;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getCreateDate() {
        return createDate;
    }

    public void setCreateDate(String createDate) {
        this.createDate = createDate;
    }
}


  • 这里如果用lombok的话,只需要一个@Data注解
@Data
public class Syslog implements Serializable {
    private String id;  //我用的全宇宙唯一的子串串、也是直接用的工具类

    private String username; //用户名

    private String operation; //操作

    private String method; //方法名

    private String createDate; //操作时间,这里可以使用Date来实现。我写的有个工具类。用的String接收

   
}

5、先写dao层和mapper层

(1)dao层

/**
 * @author 王恒杰
 * @date 2022/10/25 14:59
 * @Description:
 */
@Mapper
public interface SysLogMapper {
    /**
     * 写入日志
     * @param syslog
     * @return
     */
    int addLog(Syslog syslog);
}


(2)mapper层

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sgsg.verification.dao.SysLogMapper">
    <insert id="addLog" parameterType="com.sgsg.verification.entity.Syslog">
        insert into syslog(id, username, operation, method, createDate)
        values (#{id}, #{username}, #{operation}, #{method}, #{createDate})
    </insert>
</mapper>

6、写业务层

  • 接口层
import com.sgsg.verification.entity.Syslog;

/**
 * @author 王恒杰
 * @date 2022/10/25 14:58
 * @Description:
 */
public interface SysLogService {

    //写入日志
    int addLog(Syslog syslog);
}

  • 实现类
/**
 * @author 王恒杰
 * @date 2022/10/25 14:58
 * @Description:
 */
@Service
public class SysLogServiceImpl implements SysLogService {
    @Autowired
    public SysLogMapper sysLogMapper;
        //写入日志
        @Override
        public int addLog(Syslog syslog) {
            return sysLogMapper.addLog(syslog);
        }

}

7、切面核心方法

package com.sgsg.verification.aspect;

/**
 * @author 王恒杰
 * @date 2022/10/25 14:57
 * @Description:
 */

import com.auth0.jwt.interfaces.Claim;
import com.sgsg.verification.dao.UserDao;
import com.sgsg.verification.entity.Result;
import com.sgsg.verification.entity.Syslog;
import com.sgsg.verification.entity.UserInfoEntity;
import com.sgsg.verification.log.Mylog;
import com.sgsg.verification.service.SysLogService;
import com.sgsg.verification.service.UserService;
import com.sgsg.verification.utils.JwtUtil;
import com.sgsg.verification.utils.UserInfoUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
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 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.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 系统日志:切面处理类
 */
@Aspect
@Component
public class SysLogAspect {


    @Autowired
    private SysLogService sysLogService;//将数据写入数据库的操作
    @Autowired
    private UserDao userDao;
    @Autowired
    private JwtUtil jwtUtil;

    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.sgsg.verification.log.Mylog ))")
    public void logPoinCut() {
    }

    //切面 配置通知
    @AfterReturning("logPoinCut()")
    public void saveSysLog(JoinPoint joinPoint) {
        System.out.println("切面。。。。。");
        //保存日志
        Syslog sysLog = new Syslog();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取操作
        Mylog myLog = method.getAnnotation(Mylog.class);
        if (myLog != null) {
            String value = myLog.value();
            sysLog.setOperation(value);//保存获取的操作
        }

        //设置id
        String id = UUID.randomUUID().toString();
        sysLog.setId(id);

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        sysLog.setCreateDate(simpleDateFormat.format(new Date()));
        //获取用户名
        //拿到当前用户的信息、我这里使用的Token。直接从Token中获取当前用户信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        // 从 http 请求头中取出 token
        String token = request.getHeader("authorization");
        // 解析token并获取token中的用户信息
        Map<String, Claim> claims = jwtUtil.verity(token);
        Claim userId = claims.get("userId");
        //获取用户
        UserInfoEntity user = userDao.getInfoById(userId.asInt());
        sysLog.setUsername(user.getCname());
        //调用service保存SysLog实体类到数据库
        sysLogService.addLog(sysLog);
    }
}



8、我们自己定义注解类

package com.sgsg.verification.log;
/**
 * @author 王恒杰
 * @date 2022/10/25 14:56
 * @Description: 自定义注解类
 */
import java.lang.annotation.*;
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档

public @interface  Mylog {
    String value() default "";
}

9、在控制层使用我们自定义的注解

package com.sgsg.verification.controller;

import com.sgsg.verification.entity.Result;
import com.sgsg.verification.log.Mylog;
import com.sgsg.verification.service.EnumService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 王恒杰
 * @date 2022/10/6 12:30
 * @Description:
 */
@Slf4j
@RestController
@RequestMapping("/enums")
public class EnumController {
    @Autowired
    private EnumService enumService;
    @Mylog(value = "获取枚举")
    @GetMapping("/selectAll")
    public Result selectAll() {
        return enumService.selectAll();
    }
}

SpringBoot使用在控制层切面注解配置的方式将日志存储在mysql

10、启动项目之后我们的日志就存储在数据库里面了

SpringBoot使用在控制层切面注解配置的方式将日志存储在mysql