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

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

时间:2023-08-29 14:59:25浏览次数:42  
标签:String ip 数据库 AOP Boot request Spring 日志

本文分享自华为云社区《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中添加以下依赖:

<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文件中配置数据库连接信息:

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的配置格式:

cke_123.png

定义日志实体类

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

@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接口来拦截请求并记录日志:

@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注解来实现方法拦截:

@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

 

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

 

标签:String,ip,数据库,AOP,Boot,request,Spring,日志
From: https://www.cnblogs.com/huaweiyun/p/17664738.html

相关文章

  • Spring框架中的事件(Event)
    什么是事件机制?Spring的事件(Event)机制是一种在应用程序中实现模块之间解耦和信息传递的机制。它基于发布者-订阅者模式,通过事件的发布和监听来实现组件之间的通信。基本概念:事件类(EventClass):在Spring框架中,事件是通过定义一个继承自ApplicationEvent类的自定义事件类来表示的。这......
  • Spring Boot - 引入 validation 对参数或实体类进行校验不管用
    版本说明我的父工程版本号是3.1.0。file:[pom.xml]<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.0</version><relativePath/><!--......
  • spring boot WebSocket @ServerEndpoint注解标识的class无法获取spring容器中的bean
    在@ServerEndpoint类中直接使用@Autowired注解注入Spring管理的bean可能不会成功,因为@ServerEndpoint并不受Spring容器的管理。通过创建一个静态的成员遍历属性和一个带有@Autowired注解的setter方法,你可以在类加载时将bean注入到静态属性中。但是,请注意这样做......
  • Spring JdbcTemplate
    什么是JdbcTemplate大家来回顾一下,java中操作db最原始的方式就是纯jdbc了,是不是每次操作db都需要加载数据库驱动、获取连接、获取PreparedStatement、执行sql、关闭PreparedStatement、关闭连接等等,操作还是比较繁琐的。spring中提供了一个模块,对jdbc操作进行了封装,使其更简单,......
  • 架构之选:评价Spring Cloud在微服务完整性方面的独到观点!
    大家好,我是小米!今天我们要来聊一个非常热门的话题:SpringCloud在微服务架构方面的完整度情况。随着技术的不断演进,微服务架构已经成为了众多企业构建灵活、可扩展系统的首选。而SpringCloud作为微服务架构的佼佼者,自然成为了我们必须深入了解的对象。废话不多说,咱们开始吧!前言:微服......
  • 基于SpringBoot的装饰工程管理系统
    如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统装饰工程项目信息管理难度大,容错率低,管理人员处理数据费工费时,所以专门为解决这个难题开发了一个装饰工程管理系统项目立......
  • Spring Boot 别再用 Date 作为入参了,LocalDateTime、LocalDate 真香!
    作者:TinyThing链接:https://www.jianshu.com/p/b52db905f0200x0背景项目中使用LocalDateTime系列作为dto中时间的类型,但是spring收到参数后总报错,为了全局配置时间类型转换,尝试了如下3中方法。注:本文基于Springboot2.0测试,如果无法生效可能是spring版本较低导致的。PS:如果你......
  • 聊聊spring项目中如何动态刷新bean
    前言前阵子和朋友聊天,他手头上有个spring单体项目,每次数据库配置变更,他都要重启项目,让配置生效。他就想说有没有什么办法,不重启项目,又可以让配置生效。当时我就跟他说,可以用配置中心,他的意思是因为是维护类项目,不想再额外引入一个配置中心,增加运维成本。后边跟他讨论了一个方案,可......
  • SpringBoot内置Tomcat的参数值
    SpringBoot内置Tomcat,在默认设置中,Tomcat的最大线程数是200,最大连接数是10000。默认情况下,支持最大并发量为一万,也就是指支持的连接数。Tomcat有两种处理连接的模式是BIO,一个线程只处理一个Socket连接是NIO,一个线程处理多个Socket连接处理多个连接的单个线程通常不会引起太......
  • Springboot——后端的一些配置(大部分都用得到)
    <repositories><repository><id>nexus-aliyun</id><name>nexus-aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><rele......