SpringBoot中統(tǒng)計方法耗時的七種實現方式小結
一、手動使用StopWatch
最直接的方法是使用Spring提供的StopWatch類,這種方式簡單直觀,適合臨時性的性能測試。
import org.springframework.util.StopWatch;
@Service
public class UserService {
public User findUserById(Long id) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 業(yè)務邏輯
User user = userRepository.findById(id).orElse(null);
stopWatch.stop();
System.out.println("findUserById方法耗時:" + stopWatch.getTotalTimeMillis() + "ms");
return user;
}
}
優(yōu)點:簡單直觀,無需額外配置
缺點:侵入業(yè)務代碼,不夠優(yōu)雅,需要手動添加到每個需要監(jiān)控的方法
二、使用AOP實現全局方法耗時統(tǒng)計
AOP(面向切面編程)是實現方法耗時統(tǒng)計的理想選擇,它可以在不修改原有代碼的情況下,統(tǒng)一處理耗時統(tǒng)計邏輯。
首先,添加AOP依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然后,創(chuàng)建切面類:
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();
// 執(zhí)行目標方法
Object result = joinPoint.proceed();
stopWatch.stop();
String methodName = joinPoint.getSignature().getName();
System.out.println("方法[" + methodName + "]耗時:" + stopWatch.getTotalTimeMillis() + "ms");
return result;
}
}
優(yōu)點:代碼無侵入,統(tǒng)一管理,配置靈活
缺點:對于特定方法的定制化需求不夠靈活
三、自定義注解+AOP實現更精細的控制
這種方法結合了自定義注解和AOP,可以更精確地控制哪些方法需要進行耗時統(tǒng)計。
首先,創(chuàng)建自定義注解:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeLog {
String value() default "";
}
然后,創(chuàng)建切面類處理帶有該注解的方法:
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) {
// 業(yè)務邏輯
return productRepository.findById(id).orElse(null);
}
}
優(yōu)點:更精細的控制,注解可攜帶更多信息,便于定制
缺點:需要手動在方法上添加注解
四、使用攔截器統(tǒng)計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/");
}
}
優(yōu)點:專注于Web接口性能,對接口進行統(tǒng)一監(jiān)控
缺點:只能監(jiān)控Controller層方法,無法監(jiān)控內部服務方法
五、使用Actuator + Micrometer實現細粒度監(jiān)控
Spring Boot Actuator提供了與Micrometer的集成,可以實現更專業(yè)的性能指標收集:
添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
使用Micrometer進行方法計時:
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final MeterRegistry meterRegistry;
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public Order createOrder(OrderRequest request) {
Timer.Sample sample = Timer.start(meterRegistry);
// 業(yè)務邏輯
Order order = processOrder(request);
sample.stop(meterRegistry.timer("order.creation.time"));
return order;
}
}
配置Actuator暴露指標:
management:
endpoints:
web:
exposure:
include: metrics,prometheus
metrics:
export:
prometheus:
enabled: true
優(yōu)點:專業(yè)的性能指標收集,可與Prometheus、Grafana等監(jiān)控系統(tǒng)集成,適合生產環(huán)境
缺點:配置相對復雜,有一定學習成本
六、使用Filter實現請求耗時統(tǒng)計
創(chuàng)建一個Filter實現類,可以記錄每次HTTP請求的開始時間和結束時間,從而計算出請求的整體耗時。
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
@Component
public class TimingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
// 繼續(xù)處理請求
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
String requestUri = ((HttpServletRequest) request).getRequestURI();
System.out.println("請求[" + requestUri + "]耗時:" + executionTime + "ms");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
優(yōu)點:可以全局監(jiān)控所有Web請求的耗時。
缺點:只提供整體請求的耗時,無法深入到具體業(yè)務邏輯的執(zhí)行時間。
七、使用ServletRequestHandledEvent統(tǒng)計請求處理耗時
Spring Boot提供了ServletRequestHandledEvent事件,可以用來監(jiān)控HTTP請求的處理時間。這種方式適合于全局監(jiān)控所有的請求。
首先,創(chuàng)建事件監(jiān)聽器:
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.request.ServletRequestHandledEvent;
import org.springframework.stereotype.Component;
@Component
public class RequestTimingListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
System.out.println("請求[" + event.getRequestUrl() + "]耗時:" + event.getProcessingTimeMillis() + "ms");
}
}
這種方法會自動監(jiān)聽處理結果,不需要在每個Controller中進行顯式的耗時統(tǒng)計。
優(yōu)點:不需要修改現有代碼,監(jiān)控全局請求的耗時
缺點:不支持自定義請求的粒度控制
總結與對比
在SpringBoot中,以上七種方法各有優(yōu)缺點,可以根據不同的場景選擇合適的方案:
- StopWatch手動統(tǒng)計:適合臨時測試,快速實現
- 全局AOP:適合對整個服務層進行性能監(jiān)控
- 自定義注解+AOP:適合精細化控制,只監(jiān)控關鍵方法
- 攔截器:適合Web接口監(jiān)控
- Actuator+Micrometer:適合生產環(huán)境,與專業(yè)監(jiān)控系統(tǒng)集成
- Filter:適合全局請求監(jiān)控,輕量級實現
- ServletRequestHandledEvent:全局監(jiān)控HTTP請求處理時間,不需改動代碼
以上就是SpringBoot中統(tǒng)計方法耗時的七種實現方式小結的詳細內容,更多關于SpringBoot統(tǒng)計方法耗時的資料請關注腳本之家其它相關文章!
相關文章
Spring中allowedOriginPatterns和allowedOrigins方法有何不同詳解
這篇文章主要給大家介紹了關于Spring中allowedOriginPatterns和allowedOrigins方法有何不同,allowedOriginPatterns和allowedOrigins都是用來設置允許跨域請求的來源,需要的朋友可以參考下2023-10-10

