代码主要目的是controller方法进行日志记录,记录请求的内容、调用的方法、参数以及响应的内容和请求处理的时间。
1.介绍
AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性,允许开发者定义跨多个对象的横切关注点。
在Spring Boot中,AOP的使用几个步骤:
- 定义Aspect:Aspect是包含一些advice(通知)的类。通知是实际执行的代码,它可以是一个方法或者一个lambda表达式。在Aspect中,你可以定义前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)等。
- 配置AspectJ自动代理:
- 定义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.整体代码介绍
用于记录请求和响应的日志。
- 定义Aspect:
AccessLogAspect
是一个Aspect,它包含了前置通知(Before)、后置通知(AfterReturning)功能。 - Pointcut定义:通过
@Pointcut
注解定义了一个切入点表达式,用于匹配com.up.cloud
和com.up.cloud.server
包下的所有public controller方法。 - 前置通知(Before):
-
@Before("invokeLog()")
:在匹配到切入点的方法执行之前,执行前置通知的方法。 - 在
doBefore
方法中,首先获取了当前的请求信息,并记录了请求的URL、调用的类和方法以及请求参数。 - 同时,还记录了当前的时间戳,用于后续计算请求处理时间。
- 后置通知(AfterReturning):
-
@AfterReturning(returning = "ret", pointcut = "invokeLog()")
:在匹配到切入点的方法执行之后且在返回结果之前,执行后置通知的方法。 - 在
doAfterReturning
方法中,首先将返回的结果转换为JSON字符串并记录下来。 - 计算并记录了请求处理的时间。
- 最后清除了之前记录的起始时间。
- 日志记录:使用SLF4J的Logger来记录日志,记录了请求的URL、方法、参数以及响应的内容和请求处理的时间。
- 线程局部变量:使用
ThreadLocal
来存储起始时间,这样每个线程的起始时间都是独立的,不会互相干扰。
代码主要目的是对controller方法进行日志记录,记录请求的内容、调用的方法、参数以及响应的内容和请求处理的时间。