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

简介: 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日志并进行多维度分析。
相关文章
|
15天前
|
API Java 监控
SpringBoot基于OpenAPI3的接口文档管理快速集成和使用
本文主要简单介绍SpringCloud2023中进行接口文档管理,方便前后端开发和文档维护。文档管理工具基于开源的knife4j封装的openapi3。
47 3
|
25天前
|
Java Spring
SpringBoot接收参数的方式
本文介绍了Java Spring Boot中处理GET请求的多种方式。可以使用方法参数直接接收,或者通过`@RequestParam`注解指定必传参数。此外,可以用实体类接收多个相关参数,但不能同时使用`@RequestParam`。还可以通过`HttpServletRequest`对象获取参数,或利用`@PathVariable`接收路径变量。对于数组和集合参数,需注意使用`@RequestParam`注解。
134 1
|
3天前
|
Prometheus Cloud Native Java
springboot集成prometheus异常处理
springboot集成prometheus异常处理
11 2
|
6天前
|
移动开发 前端开发 JavaScript
|
6天前
|
网络协议 JavaScript Java
|
10天前
|
Java Spring
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
|
13天前
|
搜索推荐 前端开发 JavaScript
SpringBoot静态资源访问控制和封装集成方案
该文档描述了对基于SpringBoot的项目框架进行优化和整合的过程。原先采用前后端分离,后端兼做前端,但随着项目增多,升级维护变得复杂。因此,决定整合后台管理页面与后端代码,统一发布。设计上,框架包含后台管理资源,项目则配置具体业务页面,项目可通过覆盖框架资源实现个性化。关键步骤包括:自定义静态资源访问路径、解决图标与字体文件访问问题、设定自定义欢迎页面和页面图标,以及确保项目能正确访问框架静态资源。通过扫描jar包、解压和拷贝资源到项目目录,实现了框架静态资源的动态加载。此外,调整静态资源访问优先级,保证正确加载。最终实现支持jar和war包的项目结构优化。
49 4
|
20天前
|
消息中间件 JSON Java
SpringBoot集成和使用消息队列
SpringBoot集成和使用消息队列
|
1月前
|
Java 数据库连接 数据安全/隐私保护
springBoot集成token认证,最全Java面试知识点梳理
springBoot集成token认证,最全Java面试知识点梳理
|
1月前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数