代码主要目的是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方法进行日志记录,记录请求的内容、调用的方法、参数以及响应的内容和请求处理的时间。