SpringBoot中統(tǒng)計方法耗時的七種實現(xiàn)方式小結(jié)
一、手動使用StopWatch
最直接的方法是使用Spring提供的StopWatch
類,這種方式簡單直觀,適合臨時性的性能測試。
import org.springframework.util.StopWatch; @Service public class UserService { public User findUserById(Long id) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 業(yè)務(wù)邏輯 User user = userRepository.findById(id).orElse(null); stopWatch.stop(); System.out.println("findUserById方法耗時:" + stopWatch.getTotalTimeMillis() + "ms"); return user; } }
優(yōu)點:簡單直觀,無需額外配置
缺點:侵入業(yè)務(wù)代碼,不夠優(yōu)雅,需要手動添加到每個需要監(jiān)控的方法
二、使用AOP實現(xiàn)全局方法耗時統(tǒng)計
AOP(面向切面編程)是實現(xiàn)方法耗時統(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í)行目標(biāo)方法 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實現(xiàn)更精細(xì)的控制
這種方法結(jié)合了自定義注解和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è)務(wù)邏輯 return productRepository.findById(id).orElse(null); } }
優(yōu)點:更精細(xì)的控制,注解可攜帶更多信息,便于定制
缺點:需要手動在方法上添加注解
四、使用攔截器統(tǒng)計Controller接口耗時
如果只關(guān)注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)控內(nèi)部服務(wù)方法
五、使用Actuator + Micrometer實現(xiàn)細(xì)粒度監(jiān)控
Spring Boot Actuator提供了與Micrometer的集成,可以實現(xiàn)更專業(yè)的性能指標(biāo)收集:
添加依賴:
<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è)務(wù)邏輯 Order order = processOrder(request); sample.stop(meterRegistry.timer("order.creation.time")); return order; } }
配置Actuator暴露指標(biāo):
management: endpoints: web: exposure: include: metrics,prometheus metrics: export: prometheus: enabled: true
優(yōu)點:專業(yè)的性能指標(biāo)收集,可與Prometheus、Grafana等監(jiān)控系統(tǒng)集成,適合生產(chǎn)環(huán)境
缺點:配置相對復(fù)雜,有一定學(xué)習(xí)成本
六、使用Filter實現(xiàn)請求耗時統(tǒng)計
創(chuàng)建一個Filter實現(xiàn)類,可以記錄每次HTTP請求的開始時間和結(jié)束時間,從而計算出請求的整體耗時。
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è)務(wù)邏輯的執(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)聽處理結(jié)果,不需要在每個Controller中進行顯式的耗時統(tǒng)計。
優(yōu)點:不需要修改現(xiàn)有代碼,監(jiān)控全局請求的耗時
缺點:不支持自定義請求的粒度控制
總結(jié)與對比
在SpringBoot中,以上七種方法各有優(yōu)缺點,可以根據(jù)不同的場景選擇合適的方案:
- StopWatch手動統(tǒng)計:適合臨時測試,快速實現(xiàn)
- 全局AOP:適合對整個服務(wù)層進行性能監(jiān)控
- 自定義注解+AOP:適合精細(xì)化控制,只監(jiān)控關(guān)鍵方法
- 攔截器:適合Web接口監(jiān)控
- Actuator+Micrometer:適合生產(chǎn)環(huán)境,與專業(yè)監(jiān)控系統(tǒng)集成
- Filter:適合全局請求監(jiān)控,輕量級實現(xiàn)
- ServletRequestHandledEvent:全局監(jiān)控HTTP請求處理時間,不需改動代碼
以上就是SpringBoot中統(tǒng)計方法耗時的七種實現(xiàn)方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot統(tǒng)計方法耗時的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java如何實現(xiàn)嵌套對象轉(zhuǎn)大map(扁平化)
這篇文章主要介紹了java如何實現(xiàn)嵌套對象轉(zhuǎn)大map(扁平化),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10Java中FileWriter的用法及wirte()重載方法詳解
這篇文章主要介紹了Java中FileWriter的用法及wirte()重載方法詳解,FileWriter是Java編程語言中的一個類,用于將字符寫入文件,它提供了一種簡單而方便的方式來創(chuàng)建、打開和寫入文件,通過使用FileWriter,我們可以將字符數(shù)據(jù)寫入文本文件,需要的朋友可以參考下2023-10-10劍指Offer之Java算法習(xí)題精講字符串操作與數(shù)組及二叉搜索樹
跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化2022-03-03java:無法訪問org.springframework.boot.SpringApplication問題
這篇文章主要介紹了java:無法訪問org.springframework.boot.SpringApplication問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Java Swing JRadioButton單選按鈕具體使用
這篇文章主要介紹了Java Swing JRadioButton單選按鈕具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12