太全了!Spring Boot 3.4 七種方法耗時統計實現,一文搞定!
在日常開發過程中,我們經常會遇到系統響應變慢、頁面加載遲緩、接口卡頓等問題,而這些問題背后隱藏的往往是方法級性能瓶頸。隨著系統的不斷復雜化,代碼邏輯日益龐大,如何精準定位耗時點,及時發現并優化這些“隱形殺手”,已成為后端開發的基本功。
尤其是在 Spring Boot 3.4 日趨成熟、廣泛應用于微服務架構的背景下,開發者對方法執行時間的可觀測性和性能數據的可量化性提出了更高要求。
本篇文章將以 com.icoderoad 作為標準業務包路徑,系統梳理 七種在Spring Boot 3.4項目中統計方法執行耗時的主流實現方案,從最基礎的 StopWatch
手動記錄,到高級的 AOP 切面、再到生產級的 Micrometer + Actuator 監控體系,全方位覆蓋應用開發中的各類場景,助你輕松搞定耗時統計,提升系統穩定性與可維護性!
方式一:最簡單直接 —— StopWatch手動計時
這種方式適用于臨時調試,定位具體代碼塊性能問題時很好用。
package com.icoderoad.service;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
@Service
public class UserService {
public User findById(Long id) {
StopWatch watch = new StopWatch();
watch.start();
// 模擬業務操作
User user = userRepository.findById(id).orElse(null);
watch.stop();
System.out.println("findById 執行耗時:" + watch.getTotalTimeMillis() + "ms");
return user;
}
}
優勢:無需配置、上手簡單
劣勢:代碼侵入性強,不適合全局使用
方式二:面向切面 —— AOP統一攔截統計
借助 Spring AOP,可以無感知地實現方法耗時采集。
首先,添加 AOP 依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
切面實現如下:
package com.icoderoad.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Aspect
@Component
public class PerformanceAspect {
@Pointcut("execution(* com.icoderoad.service..*(..))")
public void serviceMethods() {}
@Around("serviceMethods()")
public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch sw = new StopWatch();
sw.start();
Object result = joinPoint.proceed();
sw.stop();
System.out.println("方法【" + joinPoint.getSignature() + "】耗時:" + sw.getTotalTimeMillis() + "ms");
return result;
}
}
優勢:完全無侵入,集中管理
劣勢:粒度控制較弱,不適合只監控個別方法
方式三:自定義注解 + AOP,精準掌控要點
如果你只想統計某些特定方法的耗時,又不想在方法里寫邏輯,推薦這種方式。
注解定義:
package com.icoderoad.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TrackTime {
String description() default "";
}
切面處理類:
package com.icoderoad.aspect;
import com.icoderoad.annotation.TrackTime;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Aspect
@Component
public class TrackTimeAspect {
@Around("@annotation(com.icoderoad.annotation.TrackTime)")
public Object handleTimedMethod(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
TrackTime annotation = signature.getMethod().getAnnotation(TrackTime.class);
String desc = annotation.description().isEmpty() ? signature.getMethod().getName() : annotation.description();
StopWatch sw = new StopWatch();
sw.start();
Object result = joinPoint.proceed();
sw.stop();
System.out.println("方法[" + desc + "]執行時間:" + sw.getTotalTimeMillis() + "ms");
return result;
}
}
使用方法:
package com.icoderoad.service;
import com.icoderoad.annotation.TrackTime;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@TrackTime(description = "獲取商品詳情")
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
}
優勢:可控性高、便于擴展
劣勢:仍需手動打注解
方式四:Spring Interceptor —— 專注于Controller層
如果你只關心 Web 層接口的耗時統計,用攔截器最合適不過了。
攔截器類:
package com.icoderoad.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.*;
@Component
public class ApiTimerInterceptor implements HandlerInterceptor {
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
startTime.set(System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
long duration = System.currentTimeMillis() - startTime.get();
System.out.println("接口【" + req.getRequestURI() + "】耗時:" + duration + "ms");
startTime.remove();
}
}
注冊攔截器:
package com.icoderoad.config;
import com.icoderoad.interceptor.ApiTimerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final ApiTimerInterceptor interceptor;
public WebMvcConfig(ApiTimerInterceptor interceptor) {
this.interceptor = interceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/api/**");
}
}
優勢:Web接口性能監控利器
劣勢:無法統計 Service 等后端業務層邏輯
方式五:Actuator + Micrometer + Prometheus,專業級監控搭檔
適合部署到生產環境,通過可視化儀表盤統一查看性能數據。
添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
服務類中記錄方法時間:
package com.icoderoad.service;
import io.micrometer.core.instrument.*;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final MeterRegistry registry;
public OrderService(MeterRegistry registry) {
this.registry = registry;
}
public Order createOrder(OrderRequest request) {
Timer.Sample sample = Timer.start(registry);
Order order = process(request); // 假設業務邏輯處理
sample.stop(registry.timer("order.create.time"));
return order;
}
}
配置 application.yml
暴露端點:
management:
endpoints:
web:
exposure:
include: prometheus,metrics
metrics:
export:
prometheus:
enabled: true
優勢:配合Grafana視圖完美呈現,適合線上
劣勢:配置偏復雜,對初學者門檻較高
方式六:Filter 過濾器方式,全鏈路請求計時
最適合做網關、統一入口類應用的方式之一。
package com.icoderoad.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class RequestTimingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long begin = System.currentTimeMillis();
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - begin;
String uri = ((HttpServletRequest) request).getRequestURI();
System.out.println("請求[" + uri + "]總耗時:" + duration + "ms");
}
}
優勢:全局統計請求,零業務侵入
劣勢:無法細分業務邏輯耗時
方式七:事件監聽器 —— 輕松統計請求執行時間
使用 Spring 自帶的事件 ServletRequestHandledEvent
,簡單高效。
package com.icoderoad.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestHandledEvent;
@Component
public class HttpRequestListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
System.out.println("請求【" + event.getRequestUrl() + "】處理時長:" + event.getProcessingTimeMillis() + "ms");
}
}
優勢:無侵入、自動觸發
劣勢:僅限于Controller層,無法擴展具體邏輯耗時
最后總結:七種方案對比參考
方式 | 優勢 | 劣勢 | 場景推薦 |
StopWatch | 快速上手 | 代碼侵入強 | 臨時測試、調優 |
AOP | 全局統一、代碼無侵入 | 粒度不夠細 | Service層整體監控 |
注解 + AOP | 靈活定制、擴展性強 | 需手動打標記 | 關鍵方法性能采集 |
攔截器 Interceptor | 專注接口、配置簡潔 | 僅限Controller層 | Web接口性能分析 |
Micrometer + Actuator | 專業監控、對接Grafana | 使用門檻高 | 生產環境實時監控 |
Filter | 全局請求層統計 | 粒度粗 | 網關、統一入口場景 |
事件監聽器 | 系統自動監聽 | 不支持業務內部耗時 | 簡易接口請求分析 |
結語:沒有監控的優化都是“空談”
在性能優化的世界里,有一句老話:“你無法優化你不了解的東西(You can't improve what you don't measure)”。無論是對接口響應的監控、還是對業務邏輯執行效率的衡量,方法耗時統計都是系統調優的重要基石。
通過本文介紹的七種耗時統計手段,開發者可以:
- 快速定位耗時點,找出性能瓶頸;
- 精細化地監控關鍵業務邏輯;
- 將系統運行指標納入可觀測體系,助力 DevOps;
- 在不侵入原有業務邏輯的前提下,實現非侵入式性能分析;
- 構建符合生產要求的“性能感知型系統”。
每種方案都有其適用場景:手動StopWatch適合快速驗證,AOP適合中大型系統的統一統計,自定義注解+AOP適合重點方法定點監控,攔截器與Filter適合接口請求統計,Micrometer/Actuator則是監控系統集成首選,事件監聽提供了無需改動代碼的全局方案。