springboot几种统计方法耗时的实现方式
前言:
说说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中,以上几种方法各有优缺点,可以根据不同的场景选择合适的方案:
- StopWatch手动统计:适合临时测试,快速实现
- 全局AOP:适合对整个服务层进行性能监控
- 自定义注解+AOP:适合精细化控制,只监控关键方法
- 拦截器:适合Web接口监控
上一篇:什么是 Java 注解?