通过AOP拦截Spring Boot日志并将其存入数据库

本文分享自华为云社区《Spring Boot入门(23):【实战】通过AOP拦截Spring Boot日志并将其存入数据库》,作者:bug菌。

前言

在软件开发中,常常需要记录系统运行时的日志。日志记录有助于排查系统问题、优化系统性能、监控操作行为等。本文将介绍如何使用Spring Boot和AOP技术实现拦截系统日志并保存到数据库中的功能。

摘要

本文将通过以下步骤实现拦截系统日志并保存到数据库中的功能:

  1. 配置数据库连接
  2. 定义日志实体类
  3. 定义日志拦截器
  4. 使用AOP拦截日志并保存到数据库中

AOP介绍

AOP,全称是Aspect Oriented Programming,即面向切面编程。AOP的目的是将那些与业务无关,但是业务模块都需要的功能,如日志统计、安全控制、事务处理等,封装成可重用的组件,从而将它们从业务逻辑代码中划分出来,编写成独立的切面。这样做,既可以保持业务逻辑的纯净和高内聚性,又可以使得系统的多个模块都可以共享这些公共的功能。

Spring框架提供了对AOP的支持,Spring Boot自然也不例外。使用Spring Boot的AOP功能,我们可以在运行时动态地将代码横向切入到各个关注点(方法或者类)中。这种横向切面的方式,比传统的纵向切面(继承)更加灵活。

AOP的实现

添加依赖

在pom.xml中添加以下依赖:

xml 复制代码
<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>

</dependency>

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

</dependency>

这样我们就可以使用Spring Boot的AOP功能和MyBatis框架。

配置数据库连接

首先需要在Spring Boot项目的application.properties文件中配置数据库连接信息:

ini 复制代码
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false

spring.datasource.username=root

spring.datasource.password=123456

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

或者你也可以使用YAML的配置格式:

定义日志实体类

定义一个Log实体类用于保存日志信息,并使用@Entity和@Table注解指定对应的数据库表和字段:

less 复制代码
@Entity

@Table(name = "sys_log")

public class Log {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

private String operation;

private String method;

private String params;

private String ip;

private Date createTime;

// 省略getter和setter方法

}

定义日志拦截器

定义一个日志拦截器LogInterceptor,通过实现HandlerInterceptor接口来拦截请求并记录日志:

scss 复制代码
@Component

public class LogInterceptor implements HandlerInterceptor {

@Autowired

private LogRepository logRepository;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

// 获取请求的IP地址

String ip = getIpAddress(request);

// 获取当前用户

String username = getCurrentUsername();

// 获取请求的方法名

String method = request.getMethod();

// 获取请求的URL

String url = request.getRequestURI();

// 获取请求的参数

String params = getParams(request);

// 创建日志实体

Log log = new Log();

log.setIp(ip);

log.setMethod(method);

log.setOperation("访问");

log.setParams(params);

log.setUsername(username);

log.setCreateTime(new Date());

// 保存日志到数据库中

logRepository.save(log);



return true;

}

// 省略实现HandlerInterceptor接口的其他方法

/**

* 获取请求的IP地址

*/

private String getIpAddress(HttpServletRequest request) {

String ip = request.getHeader("X-Forwarded-For");

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("Proxy-Client-IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("WL-Proxy-Client-IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("HTTP_CLIENT_IP");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getHeader("HTTP_X_FORWARDED_FOR");

}

if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {

ip = request.getRemoteAddr();

}

return ip;

}

/**

* 获取当前用户

*/

private String getCurrentUsername() {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication != null) {

return authentication.getName();

}

return null;

}

/**

* 获取请求的参数

*/

private String getParams(HttpServletRequest request) {

Map<String, String[]> parameterMap = request.getParameterMap();

if (parameterMap == null || parameterMap.isEmpty()) {

return null;

}

StringBuilder sb = new StringBuilder();

for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {

sb.append(entry.getKey()).append("=").append(Arrays.toString(entry.getValue())).append("&");

}

return sb.toString();

}

}

使用AOP拦截日志并保存到数据库中

使用AOP技术拦截所有Controller类中的方法,并执行LogInterceptor中的preHandle方法,记录日志并保存到数据库中。

定义一个LogAspect切面类,通过实现@Aspect注解和@Before注解来实现方法拦截:

less 复制代码
@Aspect

@Component

public class LogAspect {

@Autowired

private LogInterceptor logInterceptor;

@Pointcut("execution(public * com.example.demo.controller..*.*(..))")

public void logAspect() {}

@Before("logAspect()")

public void doBefore(JoinPoint joinPoint) {

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

if (attributes == null) {

return;

}

HttpServletRequest request = attributes.getRequest();

HttpServletResponse response = attributes.getResponse();

HandlerMethod handlerMethod = (HandlerMethod) joinPoint.getSignature();

try {

logInterceptor.preHandle(request, response, handlerMethod);

} catch (Exception e) {

e.printStackTrace();

}

}

}

代码方法介绍

  • LogInterceptor.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)方法:拦截请求并记录日志的方法。
  • LogInterceptor.getIpAddress(HttpServletRequest request)方法:获取请求的IP地址。
  • LogInterceptor.getCurrentUsername()方法:获取当前用户。
  • LogInterceptor.getParams(HttpServletRequest request)方法:获取请求的参数。
  • LogAspect.logAspect()方法:定义AOP切入点,拦截Controller类中的所有方法。
  • LogAspect.doBefore(JoinPoint joinPoint)方法:执行方法拦截操作,并调用LogInterceptor.preHandle方法来记录日志。

测试用例

可以使用Postman等工具发起请求来测试拦截器是否生效,并查看数据库中是否保存了对应的日志信息。这里就不直接演示了,毕竟使用起来非常的简单易上手。

全文小结

本文介绍了如何使用Spring Boot和AOP技术实现拦截系统日志并保存到数据库中的功能,包括配置数据库连接、定义日志实体类、定义日志拦截器、使用AOP拦截日志并保存到数据库中等步骤。通过本文的介绍,可以更好地理解Spring Boot和AOP的应用,为开发高效、稳定的系统提供参考。

注:

环境说明:Windows10 + Idea2021.3.2 + Jdk1.8 + SpringBoot 2.3.1.RELEASE

点击关注,第一时间了解华为云新鲜技术~

相关推荐
QX_hao1 小时前
【Go】--map和struct数据类型
开发语言·后端·golang
MC丶科1 小时前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
G探险者2 小时前
为何一个系统上线要经过N轮测试?带你看懂企业级发布体系
后端
TDengine (老段)3 小时前
TDengine 数学函数 DEGRESS 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)3 小时前
TDengine 数学函数 GREATEST 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
安当加密3 小时前
云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
数据库·微服务·云原生
lang201509283 小时前
Spring Boot 入门:5分钟搭建Hello World
java·spring boot·后端
爱喝白开水a4 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱
想ai抽4 小时前
深入starrocks-多列联合统计一致性探查与策略(YY一下)
java·数据库·数据仓库