springboot几种统计方法耗时的实现方式

springboot几种统计方法耗时的实现方式

编程文章jaq1232025-06-23 18:12:354A+A-

前言:

说说SpringBoot框架中实现方法耗时统计的几种方法。

一、手动使用StopWatch

最直接的方法是使用Spring提供的StopWatch类,这种方式简单直观,适合临时性的性能测试。

import org.springframework.util.StopWatch;

@Service
public class UserService {
    
    public User findUserById(Long id) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 业务逻辑
        User user = userRepository.findById(id).orElse(null);
        
        stopWatch.stop();
        System.out.println("findUserById方法耗时:" + stopWatch.getTotalTimeMillis() + "ms");
        
        return user;
    }
}

二、使用AOP实现全局方法耗时统计

AOP(面向切面编程)是实现方法耗时统计的理想选择,它可以在不修改原有代码的情况下,统一处理耗时统计逻辑。
添加AOP依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

创建切面类:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class MethodTimeAspect {
    
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceMethodPointcut() {}
    
    @Around("serviceMethodPointcut()")
    public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        stopWatch.stop();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法[" + methodName + "]耗时:" + stopWatch.getTotalTimeMillis() + "ms");
        
        return result;
    }
}

三、自定义注解+AOP实现更精细的控制

创建自定义注解:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeLog {
    String value() default "";
}

创建切面类处理带有该注解的方法:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class TimeLogAspect {
    
    @Around("@annotation(com.example.demo.annotation.TimeLog)")
    public Object timeLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        TimeLog timeLog = signature.getMethod().getAnnotation(TimeLog.class);
        
        String methodDesc = timeLog.value().isEmpty() ? 
                signature.getMethod().getName() : timeLog.value();
        
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        Object result = joinPoint.proceed();
        
        stopWatch.stop();
        System.out.println("方法[" + methodDesc + "]耗时:" + stopWatch.getTotalTimeMillis() + "ms");
        
        return result;
    }
}

使用示例:

@Service
public class ProductService {
    
    @TimeLog("查询商品详情")
    public Product getProductDetail(Long id) {
        // 业务逻辑
        return productRepository.findById(id).orElse(null);
    }
}

四、使用拦截器统计Controller接口耗时

关注Controller层的接口耗时,可以使用Spring的拦截器:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class ApiTimeInterceptor implements HandlerInterceptor {
    
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        startTime.set(System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime.get();
        String uri = request.getRequestURI();
        System.out.println("接口[" + uri + "]耗时:" + executionTime + "ms");
        startTime.remove();
    }
}

注册拦截器

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    private final ApiTimeInterceptor apiTimeInterceptor;
    
    public WebConfig(ApiTimeInterceptor apiTimeInterceptor) {
        this.apiTimeInterceptor = apiTimeInterceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(apiTimeInterceptor).addPathPatterns("/api/");
    }
}

总结

在SpringBoot中,以上几种方法各有优缺点,可以根据不同的场景选择合适的方案:

  1. StopWatch手动统计:适合临时测试,快速实现
  2. 全局AOP:适合对整个服务层进行性能监控
  3. 自定义注解+AOP:适合精细化控制,只监控关键方法
  4. 拦截器:适合Web接口监控

点击这里复制本文地址 以上内容由jaq123整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

苍茫编程网 © All Rights Reserved.  蜀ICP备2024111239号-21