SpringBoot中的7種耗時統(tǒng)計的實現(xiàn)方法與應(yīng)用場景
前言
在日常開發(fā)中,經(jīng)常會遇到一些性能問題。
比如用戶反饋:“這個頁面加載好慢啊!” 這個時候,你該怎么辦?
首先就得找出到底是哪個方法、哪段代碼執(zhí)行時間過長。
只有找到了瓶頸,才能對癥下藥進(jìn)行優(yōu)化。所以說,方法耗時統(tǒng)計是性能優(yōu)化中非常重要的一環(huán)。
接下來,我就給大家介紹七種實用的實現(xiàn)方式,從簡單到復(fù)雜,總有一種適合你!
1. System.currentTimeMillis()
這是最原始但最直接的方式,適用于快速驗證某段代碼的執(zhí)行時間。
public void doSomething() {
long start = System.currentTimeMillis();
// 模擬業(yè)務(wù)邏輯
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long end = System.currentTimeMillis();
System.out.println("方法執(zhí)行耗時:" + (end - start) + "ms");
}
優(yōu)點(diǎn)
- 無需引入任何依賴
- 簡單直觀,適合臨時調(diào)試
缺點(diǎn)
- 代碼侵入性強(qiáng)
- 多處使用時重復(fù)代碼多
- 精度受系統(tǒng)時鐘影響(可能受NTP調(diào)整干擾)
適用場景
- 本地開發(fā)調(diào)試
- 快速驗證某段邏輯耗時
注意:該方法基于系統(tǒng)時間,不適用于高精度計時。推薦使用 System.nanoTime() 替代(見后文補(bǔ)充)。
2. 使用StopWatch工具類
Spring 提供了org.springframework.util.StopWatch類,支持分段計時和格式化輸出,適合需要統(tǒng)計多個子任務(wù)耗時的場景。
import org.springframework.util.StopWatch;
public void processUserFlow() {
StopWatch stopWatch = new StopWatch("用戶處理流程");
stopWatch.start("查詢用戶");
// 查詢邏輯...
Thread.sleep(50);
stopWatch.stop();
stopWatch.start("更新緩存");
// 緩存操作...
Thread.sleep(80);
stopWatch.stop();
log.info(stopWatch.prettyPrint());
}
輸出示例:
StopWatch '用戶處理流程': running time = 130897800 ns
-----------------------------------------
ms % Task name
-----------------------------------------
50.00 38% 查詢用戶
80.00 62% 更新緩存
優(yōu)點(diǎn)
- 支持多任務(wù)分段計時
- 輸出美觀,便于分析
- 可命名任務(wù),提升可讀性
缺點(diǎn)
- 仍需手動插入代碼
- 不適用于自動化監(jiān)控
適用場景
需要分析多個步驟耗時占比的復(fù)雜流程
3. 使用AOP切面+自定義注解(推薦)
通過面向切面編程(AOP),可以實現(xiàn)對指定方法的無侵入式耗時監(jiān)控。
第一步:定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogCostTime {
String value() default ""; // 方法描述
long threshold() default 0; // 耗時閾值(ms),超過則告警
}
第二步:編寫切面
@Aspect
@Component
@Slf4j
@Order(1) // 確保優(yōu)先級
public class CostTimeAspect {
@Around("@annotation(logCostTime)")
public Object around(ProceedingJoinPoint pjp, LogCostTime logCostTime) throws Throwable {
String methodName = pjp.getSignature().getName();
String desc = logCostTime.value();
long threshold = logCostTime.threshold();
long start = System.nanoTime(); // 高精度計時
Object result;
try {
result = pjp.proceed();
} finally {
long costNanos = System.nanoTime() - start;
long costMillis = TimeUnit.NANOSECONDS.toMillis(costNanos);
// 根據(jù)閾值決定日志級別
if (threshold > 0 && costMillis > threshold) {
log.warn("方法: {}.{}({}) 耗時超閾值: {} ms (閾值: {} ms)",
pjp.getTarget().getClass().getSimpleName(), methodName, desc, costMillis, threshold);
} else {
log.info("方法: {}.{}({}) 耗時: {} ms",
pjp.getTarget().getClass().getSimpleName(), methodName, desc, costMillis);
}
}
return result;
}
}
注意:需確保項目已啟用 AOP,Spring Boot 默認(rèn)支持;否則需添加 @EnableAspectJAutoProxy。
第三步:使用注解
@Service
public class UserService {
@LogCostTime(value = "根據(jù)ID查詢用戶", threshold = 50)
public User getUserById(Long id) {
try {
Thread.sleep(100); // 模擬耗時
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return userRepository.findById(id);
}
}
輸出:
WARN ... 方法: UserService.getUserById(根據(jù)ID查詢用戶) 耗時超閾值: 102 ms (閾值: 50 ms)
優(yōu)點(diǎn)
- 低侵入:只需添加注解
- 可復(fù)用:一處定義,多處使用
- 可擴(kuò)展:支持閾值告警、慢查詢監(jiān)控等
適用場景
- 核心服務(wù)方法
- 遠(yuǎn)程調(diào)用(RPC/HTTP)
- 數(shù)據(jù)庫查詢
- 復(fù)雜計算邏輯
4. 使用Micrometer@Timed注解
Micrometer是現(xiàn)代Java應(yīng)用的事實標(biāo)準(zhǔn)指標(biāo)收集庫,與Spring Boot Actuator深度集成,支持對接 Prometheus、Grafana、Datadog 等監(jiān)控系統(tǒng)。
添加依賴
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
啟用指標(biāo)端點(diǎn)
management:
endpoints:
web:
exposure:
include: metrics, prometheus
metrics:
export:
prometheus:
enabled: true
使用@Timed注解
@Service
public class BusinessService {
@Timed(
value = "business.process.time",
description = "業(yè)務(wù)處理耗時",
percentiles = {0.5, 0.95, 0.99}
)
public void process() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
訪問 /actuator/prometheus 可看到:
# HELP business_process_time_seconds
# TYPE business_process_time_seconds summary
business_process_time_seconds_count{method="process",} 1.0
business_process_time_seconds_sum{method="process",} 0.201
優(yōu)點(diǎn)
- 標(biāo)準(zhǔn)化指標(biāo),支持多維度聚合
- 可視化展示(Grafana)
- 支持報警(Prometheus Alertmanager)
適用場景
- 生產(chǎn)環(huán)境性能監(jiān)控
- 微服務(wù)架構(gòu)下的統(tǒng)一指標(biāo)體系
5. 使用Java8的Instant與Duration
Java 8 引入了新的時間 API,更加安全和易用。
public void doSomething() {
Instant start = Instant.now();
// 業(yè)務(wù)邏輯
try {
Thread.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
log.info("耗時:{} ms", duration.toMillis());
}
優(yōu)點(diǎn)
- 使用現(xiàn)代時間 API,語義清晰
- 線程安全,避免舊 Date 的坑
缺點(diǎn)
- 仍需手動編碼
- 性能略低于
nanoTime
適用場景
偏好 Java 8+ 新特性的項目
6. 異步方法耗時統(tǒng)計CompletableFuture
對于異步任務(wù),可通過回調(diào)機(jī)制統(tǒng)計耗時。
public CompletableFuture<Void> asyncProcess() {
long start = System.nanoTime();
return CompletableFuture.runAsync(() -> {
// 模擬異步任務(wù)
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).whenComplete((result, ex) -> {
long cost = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
log.info("異步任務(wù)耗時:{} ms", cost);
});
}
優(yōu)點(diǎn)
- 適用于非阻塞場景
- 可結(jié)合線程池監(jiān)控
適用場景
- 異步消息處理
- 批量任務(wù)調(diào)度
7. 使用HandlerInterceptor統(tǒng)計 Web 請求耗時
在 Web 層通過攔截器統(tǒng)一記錄所有 Controller 請求的處理時間。
@Component
public class RequestTimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute("startTime", System.nanoTime());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Long start = (Long) request.getAttribute("startTime");
if (start != null) {
long costNanos = System.nanoTime() - start;
long costMillis = TimeUnit.NANOSECONDS.toMillis(costNanos);
String uri = request.getRequestURI();
log.info("HTTP {} {} 耗時: {} ms", request.getMethod(), uri, costMillis);
}
}
}
注冊攔截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RequestTimeInterceptor requestTimeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestTimeInterceptor);
}
}
輸出:
HTTP GET /api/user/123 耗時: 105 ms
優(yōu)點(diǎn)
- 全局覆蓋所有請求
- 無需修改業(yè)務(wù)代碼
適用場景
- Web 應(yīng)用整體性能監(jiān)控
- API 網(wǎng)關(guān)層耗時分析
總結(jié)
| 方案 | 侵入性 | 適用場景 | 是否推薦 |
|---|---|---|---|
| System.currentTimeMillis() | 高 | 臨時調(diào)試 | ?? 僅調(diào)試 |
| StopWatch | 中 | 分段計時分析 | ? |
| AOP + 自定義注解 | 低 | 核心方法監(jiān)控 | ??? 強(qiáng)烈推薦 |
| Micrometer @Timed | 低 | 生產(chǎn)監(jiān)控集成 | ??? 生產(chǎn)首選 |
| Instant + Duration | 高 | 現(xiàn)代化時間處理 | ? |
| CompletableFuture 回調(diào) | 中 | 異步任務(wù) | ? |
| HandlerInterceptor | 低 | Web 請求全局監(jiān)控 | ?? |
到此這篇關(guān)于SpringBoot中的7種耗時統(tǒng)計的實現(xiàn)方法與應(yīng)用場景的文章就介紹到這了,更多相關(guān)SpringBoot統(tǒng)計耗時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+springboot+webtrc+websocket實現(xiàn)雙人音視頻通話會議(最新推薦)
這篇文章主要介紹了vue+springboot+webtrc+websocket實現(xiàn)雙人音視頻通話會議,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2025-05-05
如何使用HttpClient發(fā)送java對象到服務(wù)器
這篇文章主要介紹了如何使用HttpClient發(fā)送java對象到服務(wù)器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
基于Java SWFTools實現(xiàn)把pdf轉(zhuǎn)成swf
這篇文章主要介紹了基于Java SWFTools實現(xiàn)把pdf轉(zhuǎn)成swf,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11
windows?java?-jar無法啟動jar包簡單的解決方法
這篇文章主要介紹了windows?java?-jar無法啟動jar包簡單的解決方法,文中通過代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用java具有一定的參考借鑒價值,需要的朋友可以參考下2024-12-12
Spring Cloud服務(wù)入口Gateway的介紹和使用問題小結(jié)
Spring Cloud Gateway是Spring Cloud的?個全新的API?關(guān)項?, 基于Spring + SpringBoot等技術(shù)開發(fā), ?的是為了替換掉Zuul,這篇文章主要介紹了Spring Cloud服務(wù)入口Gateway的介紹和使用問題小結(jié),需要的朋友可以參考下2025-03-03
IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法
這篇文章主要給大家介紹了IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過代碼示例和圖文講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02

