简述

  • 上篇文章说的是使用Arthas来分析耗时,但是整体流程还是挺繁琐的,那这篇文章我们就用AOP来封装一个记录耗时的注解吧,虽然做不到分析整个调用链路上的耗时情况,但是我们可以在我们希望记录耗时的方法上加上对应的注解,来记录对应的耗时。

代码实现

  • 那现在我们来从头编写一个TakeTimeAspect类,该类通过AOP的方式来监控方法的执行时间,并且记录相关的日志信息。首先导入AOP的依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    import java.util.HashMap;
    import java.util.Map;

    @Aspect
    @Component
    public class TakeTimeAspect {

    // ThreadLocal变量用于存储开始时间和结束时间
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    ThreadLocal<Long> endTime = new ThreadLocal<>();

    // 定义一个切入点,匹配带有@TakeTimeLog注解的方法
    @Pointcut("@annotation(com.aimc.paperreduction.common.annotation.TakeTimeLog)")
    public void TakeTime() {}

    // 前置增强方法,在方法执行前记录开始时间
    @Before("TakeTime()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    // 获得注解
    TakeTimeLog timeLog = getAnnotationLog(joinPoint);
    if (timeLog == null) {
    return;
    }

    startTime.set(System.currentTimeMillis());
    }

    // 返回后增强方法,在方法执行后记录结束时间和执行时间,并记录日志
    @AfterReturning(returning = "ret", pointcut = "TakeTime()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {
    Map<String, Object> resultMap = new HashMap<>();
    String[] names = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
    if (names.length > 0) {
    for (int i = 0; i < names.length; i++) {
    if (!(joinPoint.getArgs()[i] instanceof HttpServletRequest)) {
    resultMap.put(names[i], joinPoint.getArgs()[i]);
    }
    }
    }
    // 处理完请求后,返回内容
    log.info("Return:" + JSON.toJSONString(ret));
    endTime.set(System.currentTimeMillis());
    log.info("Execution Time:" + (endTime.get() - startTime.get()));
    }

    // 用于获取方法上的注解
    private TakeTimeLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();

    if (method != null) {
    return method.getAnnotation(TakeTimeLog.class);
    }
    return null;
    }
    }
    • ThreadLocal<Long> startTimeThreadLocal<Long> endTime:这两个是ThreadLocal变量,用于存储方法执行的开始时间和结束时间,用于计算方法的执行时间。ThreadLocal允许存储每个线程特定的数据,确保线程安全性。
    • 切点表达式:@Pointcut("@annotation(com.aimc.paperreduction.common.annotation.TakeTimeLog)"),匹配带有@TakeTimeLog注解的方法。
    • 增强方法
      1. @Before("TakeTime()"):在匹配TakeTime切入点的方法之前执行。它捕获方法执行开始的时间,当然这里也可以获取更多的信息,例如方法参数之类的信息都可以拿到,这里我们根据自身项目的需求来编写。
      2. @AfterReturning(returning = "ret", pointcut = "TakeTime()"):这是一个返回后增强方法。它捕获方法的结束时间,记录返回值,并计算并记录执行时间。
    • getAnnotationLog:这是一个私有方法,用于从目标方法中获取TakeTimeLog注解(如果存在)。

使用方式

  • 我们在需要记录耗时的方法上加上此注解即可
1
2
3
4
5
6
@TakeTimeLog
public Wrapper<UploadPaperDTO> uploadDoc(MultipartFile file) throws IOException {
/**
* 你的代码逻辑
*/
}