SpringAOP实现接口请求记录到数据库的示例代码

1.引入AOP依赖

 <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-aop</artifactId>
          </dependency>

2.创建日志记录表

DROP TABLE IF EXISTS `rule_operate_log`;
CREATE TABLE `rule_operate_log`  (
  id INT(11) NOT NULL AUTO_INCREMENT COMMENT '日志id',
  path VARCHAR(4000) NULL DEFAULT NULL COMMENT '接口地址',
  http_method VARCHAR(32) NULL DEFAULT NULL COMMENT '请求方法',
  status_code VARCHAR(32) NULL DEFAULT NULL COMMENT '请求返回状态码',
  create_time_char VARCHAR(32) NULL DEFAULT NULL COMMENT '日志时间',
  create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '日志时间戳',
  ip varchar(200) NULL DEFAULT NULL COMMENT '请求ip',
  params mediumtext NULL COMMENT '请求参数',
  result mediumtext NULL COMMENT '返回值',
  exception mediumtext NULL COMMENT '接口异常',
  user_id VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户',
  user_account VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户账号',
  user_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作用户名称',
  user_org_id VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户机构id',
  user_org_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作用户机构名称',
  operate_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作名称',
  operate_position VARCHAR(200) NULL DEFAULT NULL COMMENT '操作位置',
  log_type VARCHAR(32) NULL DEFAULT NULL COMMENT '日志类型 error:错误日志 operate:操作日志',
  category_id VARCHAR(32) NULL DEFAULT NULL COMMENT '分类机构id',
  cost INT(11) NULL DEFAULT NULL COMMENT '接口耗时',
  PRIMARY KEY (id)
) COMMENT = '操作日志表';

3.日志实体类

import java.util.Date;

public class RuleOperateLog {
  /**
   * id
   */
  private Integer id;
  /**
   * 接口地址
   */
  private String path;
  /**
   * 请求方法
   */
  private String httpMethod;
  /**
   * 请求返回状态码
   */
  private String statusCode;
  /**
   * 日志时间
   */
  private String createTimeChar;
  /**
   * 日志时间戳
   */
  private Date createTime;
  /**
   * 请求ip
   */
  private String ip;
  /**
   * 请求参数
   */
  private String params;
  /**
   * 返回值
   */
  private String result;
  /**
   * 接口异常
   */
  private String exception;
  /**
   * 操作用户
   */
  private String userId;
  /**
   * 操作用户账号
   */
  private String userAccount;
  /**
   * 操作用户名称
   */
  private String userName;
  /**
   * 操作用户机构
   */
  private String userOrgId;
  /**
   * 操作用户机构名称
   */
  private String userOrgName;
  /**
   * 操作名称
   */
  private String operateName;
  /**
   * 操作位置
   */
  private String operatePosition;
  /**
   * 日志类型
   */
  private String logType;
  /**
   * 分类机构id
   */
  private String categoryId;
  /**
   * 请求耗时
   */
  private Integer cost;

  public Integer getId() {
      return id;
  }

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

  public String getPath() {
      return path;
  }

  public void setPath(String path) {
      this.path = path;
  }

  public String getHttpMethod() {
      return httpMethod;
  }

  public void setHttpMethod(String httpMethod) {
      this.httpMethod = httpMethod;
  }

  public String getStatusCode() {
      return statusCode;
  }

  public void setStatusCode(String statusCode) {
      this.statusCode = statusCode;
  }

  public String getCreateTimeChar() {
      return createTimeChar;
  }

  public void setCreateTimeChar(String createTimeChar) {
      this.createTimeChar = createTimeChar;
  }

  public Date getCreateTime() {
      return createTime;
  }

  public void setCreateTime(Date createTime) {
      this.createTime = createTime;
  }

  public String getIp() {
      return ip;
  }

  public void setIp(String ip) {
      this.ip = ip;
  }

  public String getParams() {
      return params;
  }

  public void setParams(String params) {
      this.params = params;
  }

  public String getResult() {
      return result;
  }

  public void setResult(String result) {
      this.result = result;
  }

  public String getException() {
      return exception;
  }

  public void setException(String exception) {
      this.exception = exception;
  }

  public String getUserId() {
      return userId;
  }

  public void setUserId(String userId) {
      this.userId = userId;
  }

  public String getUserAccount() {
      return userAccount;
  }

  public void setUserAccount(String userAccount) {
      this.userAccount = userAccount;
  }

  public String getUserName() {
      return userName;
  }

  public void setUserName(String userName) {
      this.userName = userName;
  }

  public String getUserOrgId() {
      return userOrgId;
  }

  public void setUserOrgId(String userOrgId) {
      this.userOrgId = userOrgId;
  }

  public String getUserOrgName() {
      return userOrgName;
  }

  public void setUserOrgName(String userOrgName) {
      this.userOrgName = userOrgName;
  }

  public String getOperateName() {
      return operateName;
  }

  public void setOperateName(String operateName) {
      this.operateName = operateName;
  }

  public String getOperatePosition() {
      return operatePosition;
  }

  public void setOperatePosition(String operatePosition) {
      this.operatePosition = operatePosition;
  }

  public String getLogType() {
      return logType;
  }

  public void setLogType(String logType) {
      this.logType = logType;
  }

  public String getCategoryId() {
      return categoryId;
  }

  public void setCategoryId(String categoryId) {
      this.categoryId = categoryId;
  }

  public Integer getCost() {
      return cost;
  }

  public void setCost(Integer cost) {
      this.cost = cost;
  }
}

4.Dao+Mapper+service

import com.xxx.xxx.xxx.entity.RuleOperateLog;

/**
* 操作日志(RuleOperateLog)表数据库访问层
*
* @author hx
* @since 2022-08-23
*/
public interface RuleOperateLogDao {

  /**
   * 新增数据
   *
   * @param operateLog
   * @return
   */
  int insert(RuleOperateLog operateLog);
}
<?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.xxx.xxx.xxx.dao.RuleOperateLogDao">

  <resultMap type="com.xxx.xxx.xxx.entity.RuleOperateLog" id="RuleOperateLogMap">
      <result property="id" column="id" jdbcType="INTEGER"/>
      <result property="path" column="path" jdbcType="VARCHAR"/>
      <result property="httpMethod" column="http_method" jdbcType="VARCHAR"/>
      <result property="statusCode" column="status_code" jdbcType="VARCHAR"/>
      <result property="createTimeChar" column="create_time_char" jdbcType="VARCHAR"/>
      <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
      <result property="ip" column="ip" jdbcType="VARCHAR"/>
      <result property="params" column="params" jdbcType="VARCHAR"/>
      <result property="result" column="result" jdbcType="VARCHAR"/>
      <result property="exception" column="exception" jdbcType="VARCHAR"/>
      <result property="userId" column="user_id" jdbcType="VARCHAR"/>
      <result property="userAccount" column="user_account" jdbcType="VARCHAR"/>
      <result property="userName" column="user_name" jdbcType="VARCHAR"/>
      <result property="userOrgId" column="user_org_id" jdbcType="VARCHAR"/>
      <result property="userOrgName" column="user_org_name" jdbcType="VARCHAR"/>
      <result property="operateName" column="operate_name" jdbcType="VARCHAR"/>
      <result property="operatePosition" column="operate_position" jdbcType="VARCHAR"/>
      <result property="logType" column="log_type" jdbcType="VARCHAR"/>
      <result property="categoryId" column="category_id" jdbcType="VARCHAR"/>
      <result property="cost" column="cost" jdbcType="INTEGER"/>
  </resultMap>

  <insert id="insert" keyProperty="id" useGeneratedKeys="true">
      insert into rule_operate_log (id, path, http_method, status_code, create_time_char, create_time,
                                    ip, params, result, exception, user_id, user_account, user_name, user_org_id,
                                    user_org_name, operate_name, operate_position, log_type, category_id, cost)
      values (#{id}, #{path}, #{httpMethod}, #{statusCode}, #{createTimeChar}, #{createTime}, #{ip}, #{params}, #{result},
              #{exception},#{userId}, #{userAccount}, #{userName}, #{userOrgId}, #{userOrgName}, #{operateName}, #{operatePosition},
              #{logType}, #{categoryId}, #{cost})
  </insert>

</mapper>
import com.xxx.xxx.xxx.entity.RuleOperateLog;

/**
* 操作日志(RuleOperateLog)表服务接口
*
* @author hx
* @since 2022-08-23
*/
public interface RuleOperateLogService {

  /**
   * 保存日志
   *
   * @param ruleOperateLog
   * @return
   */
  void saveLog(RuleOperateLog ruleOperateLog);

}
import com.xxx.xxx.xxx.dao.RuleOperateLogDao;
import com.xxx.xxx.xxx.entity.RuleOperateLog;
import com.xxx.xxx.xxx.service.RuleOperateLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* 操作日志(RuleOperateLog)表服务实现类
*
* @author hx
* @since 2022-08-23
*/
@Service("RuleOperateLogService")
public class RuleOperateLogServiceImpl implements RuleOperateLogService {

  @Autowired
  private RuleOperateLogDao operateLogDao;

  @Override
  public void saveLog(RuleOperateLog ruleOperateLog) {
      operateLogDao.insert(ruleOperateLog);
  }
}

5.自定义注解

import java.lang.annotation.*;

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogResource {
  /**
   * 服务名称
   * @return
   */
  String name();

  /**
   * 操作位置描述
   * @return
   */
  String position() default "";

  /**
   * 日志类型
   * @return
   */
  String logType() default "";
}

6.操作日志切面类

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.com.xxx.xxx.xxx.annotation.LogResource;
import com.com.xxx.xxx.xxx.constants.LogTypeConstants;
import com.com.xxx.xxx.xxx.entity.RuleOperateLog;
import com.com.xxx.xxx.xxx.service.RuleOperateLogService;
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.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Async;
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 javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* 操作日志切面类
*
* @author hx
* @since 2022-08-23
*/

@Aspect
@Component
public class OperateLogAspect {

  @Autowired
  private RuleOperateLogService operateLogService;

  //扫描使用@LogResource注解的方法
  @Pointcut("@annotation(com.com.xxx.xxx.xxx.annotation.LogResource)")
  public void logPointCut() { };

  @Around("logPointCut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {
      Date startTime = new Date();
      String exception = null;
      String result = null;
      try {
          Object obj = point.proceed();
          if (obj != null) {
              result = JSONObject.toJSONString(obj);
          }
          return obj;
      } catch (Exception e) {
          //请求时报错
          exception = e.toString();
          throw e;
      } finally {
          //操作和报错日志都记录
          HttpServletResponse response
                  = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
          int statusCode = response.getStatus();
          if (exception != null) {
              /** CHECKSTYLE:OFF:MagicNumber */
              statusCode = 500;
              /** CHECKSTYLE:ON:MagicNumber */
          }
          syncSaveLog(point, startTime, new Date(), exception, result, statusCode);
      }
  }

  @Async
  void syncSaveLog(ProceedingJoinPoint joinPoint, Date startTime, Date endTime,
                   String exception, String result, int statusCode) {
      RuleOperateLog log = new RuleOperateLog();
      try {
          MethodSignature signature = (MethodSignature) joinPoint.getSignature();
          Method method = signature.getMethod();
          LogResource annotation = method.getAnnotation(LogResource.class);
          if (annotation != null) {
              //注解上的描述
              log.setOperateName(annotation.name());
          }
          Date nowDate = new Date();
          log.setCreateTimeChar(new SimpleDateFormat("yyyyMMddhhmmss").format(nowDate));
          log.setCreateTime(nowDate);
          //入参
          if (joinPoint.getArgs() != null) {
              try {
                  log.setParams(JSONObject.toJSONString(joinPoint.getArgs(),
                          SerializerFeature.IgnoreNonFieldGetter));
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

          Long cost = endTime.getTime() - startTime.getTime();
          log.setCost(cost.intValue());
          HttpServletRequest request
                  = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
          if (request != null) {
              log.setUserName(request.getHeader(HttpHeaders.USER_AGENT));
              log.setPath(request.getRequestURI());
              log.setHttpMethod(request.getMethod());
              log.setIp(request.getRemoteAddr());
          }
          log.setStatusCode(String.valueOf(statusCode));
          log.setResult(result);
          /** CHECKSTYLE:OFF:MagicNumber */
          if (statusCode > 400 && exception != null) {
              log.setException(exception);
              log.setLogType(LogTypeConstants.ERROR);
          } else {
              log.setLogType(LogTypeConstants.OPERATE);
          }
          /** CHECKSTYLE:ON:MagicNumber */
          operateLogService.saveLog(log);
      } catch (Exception e) {
          e.printStackTrace();
      }

/*        //启动一个线程,执行报错日志防止影响主请求
      new Thread() {
          @Override
          public void run() {
              try {
                  //保存到数据库
                  operLogMapper.insertOper(operLog);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }.start();*/
  }
}

7.使用

关于Spring AOP实现接口请求记录到数据库的文章就介绍至此,更多相关Spring AOP接口请求记录数据库内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 引言因业务需要, 公司内需要使用 SpringBoot Starter 构建 SDK. 不同的是使用了更为灵活的 Kotlin 语言, 构建脚本也换成了 ...