SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间

代码主要目的是controller方法进行日志记录,记录请求的内容、调用的方法、参数以及响应的内容和请求处理的时间。

1.介绍

AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性,允许开发者定义跨多个对象的横切关注点。

在Spring Boot中,AOP的使用几个步骤:

  1. 定义Aspect:Aspect是包含一些advice(通知)的类。通知是实际执行的代码,它可以是一个方法或者一个lambda表达式。在Aspect中,你可以定义前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)等。
  2. 配置AspectJ自动代理
  3. 定义Pointcut表达式:Pointcut表达式定义了通知何时执行。你可以通过定义方法签名、类名、包名等来定义Pointcut表达式。
package com.up.cloud.core.aspect;
 
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.Arrays;
 
/**
 * @author liu pei
 * @date 2023年11月24日 下午7:12
 * @Description:
 */
@Aspect
@Component
public class AccessLogAspect {
    private static final Logger log = LoggerFactory.getLogger(AccessLogAspect.class);
 
    /**
     * 起始时间的时间戳
     */
    private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
 
    @Pointcut("execution(public * com.up.cloud.*.controller..*.*(..)) || execution(public * com.up.cloud.server.*.controller..*.*(..))")
    public void invokeLog() {
        // do something
    }
 
    @Before("invokeLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        //String args = Arrays.toString(joinPoint.getArgs());
        StringUtils.join(joinPoint.getArgs(),"");
        log.info(" Request URL : [{}, {}]", request.getMethod(), request.getRequestURL());
        log.info(" Class : [{}] , Method : [{}()]", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        log.info(" Request Param : {}", JSONUtil.toJsonStr(joinPoint.getArgs()));
        START_TIME.set(System.currentTimeMillis());
 
    }
 
    @AfterReturning(returning = "ret", pointcut = "invokeLog()")
    public void doAfterReturning(Object ret) {
        // 处理完请求,返回内容
        String jsonStr = JSONUtil.parseObj(ret).toString();
        log.info(" Response Body : {}", jsonStr.length() > 500 ? jsonStr.substring(0, 499) : jsonStr);
        log.info(" Response Time : {} ms ", (System.currentTimeMillis() - START_TIME.get()));
 
        START_TIME.remove();
    }
}

2.代码解释:

在AspectJ的AOP(面向切面编程)中,execution是一个特殊的Pointcut Designator (PDC)。它用于匹配方法的执行。
 
在给定的代码中:
 
java
@Pointcut("execution(public * com.up.cloud.*.controller..*.*(..)) || execution(public * com.up.cloud.server.*.controller..*.*(..))")  
public void invokeLog() {  
    // do something  
}
意思是匹配com.up.cloud`包及其子包下的所有public方法的执行。
其中:
 
 
*:表示方法名,第一个*表示方法名的第一个字符可以是任何字符,
第二个*表示方法名的第二个字符也可以是任何字符,
第三个*表示方法名的第三个字符可以是任何字符。
所以,.*(...)可以匹配任何方法名。
com.up.cloud.*.controller..*:表示类名。
第一个*表示com.up.cloud下的任意子包名,
第二个*表示该子包下的任意类名,第三个和第四个*分别表示类名的第二和第三个字符可以是任意字符。
所以,.controller..*可以匹配所有以controller结尾的类名。
这个Pointcut会匹配所有在com.up.cloud和com.up.cloud.server包及其子包下的
public controller类的方法执行。
 
简单地说,execution表示匹配某个方法的执行。

3.整体代码介绍

用于记录请求和响应的日志。

  1. 定义AspectAccessLogAspect 是一个Aspect,它包含了前置通知(Before)、后置通知(AfterReturning)功能。
  2. Pointcut定义:通过 @Pointcut 注解定义了一个切入点表达式,用于匹配com.up.cloudcom.up.cloud.server包下的所有public controller方法。
  3. 前置通知(Before)
  • @Before("invokeLog()"):在匹配到切入点的方法执行之前,执行前置通知的方法。
  • doBefore 方法中,首先获取了当前的请求信息,并记录了请求的URL、调用的类和方法以及请求参数。
  • 同时,还记录了当前的时间戳,用于后续计算请求处理时间。
  1. 后置通知(AfterReturning)
  • @AfterReturning(returning = "ret", pointcut = "invokeLog()"):在匹配到切入点的方法执行之后且在返回结果之前,执行后置通知的方法。
  • doAfterReturning 方法中,首先将返回的结果转换为JSON字符串并记录下来。
  • 计算并记录了请求处理的时间。
  • 最后清除了之前记录的起始时间。
  1. 日志记录:使用SLF4J的Logger来记录日志,记录了请求的URL、方法、参数以及响应的内容和请求处理的时间。
  2. 线程局部变量:使用 ThreadLocal 来存储起始时间,这样每个线程的起始时间都是独立的,不会互相干扰。

代码主要目的是对controller方法进行日志记录,记录请求的内容、调用的方法、参数以及响应的内容和请求处理的时间。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
Java 网络架构 Spring
springboot中restful风格请求的使用
本文介绍了在Spring Boot中如何使用RESTful风格的请求,包括创建HTML表单页面、在application.yaml配置文件中开启REST表单支持、编写Controller层及对应映射处理,并进行服务启动和访问测试。HTML表单默认只支持GET和POST请求,因此对于DELETE和PUT请求,需要使用隐藏域`_method`来支持。
springboot中restful风格请求的使用
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
54 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
24天前
|
前端开发 Java 测试技术
深入剖析:Spring Boot Controller中请求处理方法的访问修饰符
【10月更文挑战第21天】 在Spring Boot应用中,Controller类中的请求处理方法通常用于处理HTTP请求。这些方法的访问修饰符(private或public)对方法的行为和可访问性有着重要影响。本文将深入探讨在Controller中使用private和public修饰符的区别,以及它们对Spring MVC框架的影响。
25 8
|
19天前
|
JavaScript 前端开发 Java
SpringBoot项目的html页面使用axios进行get post请求
SpringBoot项目的html页面使用axios进行get post请求
40 2
|
19天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
34 1
|
1月前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
168 2
|
1月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
53 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
1月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
61 2
|
2月前
|
JavaScript 前端开发 Java
SpringBoot项目的html页面使用axios进行get post请求
SpringBoot项目的html页面使用axios进行get post请求
46 6