SpringBoot中統(tǒng)計(jì)方法耗時(shí)的七種實(shí)現(xiàn)方式小結(jié)
一、手動(dòng)使用StopWatch
最直接的方法是使用Spring提供的StopWatch類,這種方式簡(jiǎn)單直觀,適合臨時(shí)性的性能測(cè)試。
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方法耗時(shí):" + stopWatch.getTotalTimeMillis() + "ms");
return user;
}
}
優(yōu)點(diǎn):簡(jiǎn)單直觀,無(wú)需額外配置
缺點(diǎn):侵入業(yè)務(wù)代碼,不夠優(yōu)雅,需要手動(dòng)添加到每個(gè)需要監(jiān)控的方法
二、使用AOP實(shí)現(xiàn)全局方法耗時(shí)統(tǒng)計(jì)
AOP(面向切面編程)是實(shí)現(xiàn)方法耗時(shí)統(tǒng)計(jì)的理想選擇,它可以在不修改原有代碼的情況下,統(tǒng)一處理耗時(shí)統(tǒng)計(jì)邏輯。
首先,添加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 + "]耗時(shí):" + stopWatch.getTotalTimeMillis() + "ms");
return result;
}
}
優(yōu)點(diǎn):代碼無(wú)侵入,統(tǒng)一管理,配置靈活
缺點(diǎn):對(duì)于特定方法的定制化需求不夠靈活
三、自定義注解+AOP實(shí)現(xiàn)更精細(xì)的控制
這種方法結(jié)合了自定義注解和AOP,可以更精確地控制哪些方法需要進(jìn)行耗時(shí)統(tǒng)計(jì)。
首先,創(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 + "]耗時(shí):" + 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)點(diǎn):更精細(xì)的控制,注解可攜帶更多信息,便于定制
缺點(diǎn):需要手動(dòng)在方法上添加注解
四、使用攔截器統(tǒng)計(jì)Controller接口耗時(shí)
如果只關(guān)注Controller層的接口耗時(shí),可以使用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 + "]耗時(shí):" + executionTime + "ms");
startTime.remove();
}
}
注冊(cè)攔截器:
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)點(diǎn):專注于Web接口性能,對(duì)接口進(jìn)行統(tǒng)一監(jiān)控
缺點(diǎn):只能監(jiān)控Controller層方法,無(wú)法監(jiān)控內(nèi)部服務(wù)方法
五、使用Actuator + Micrometer實(shí)現(xiàn)細(xì)粒度監(jiān)控
Spring Boot Actuator提供了與Micrometer的集成,可以實(shí)現(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進(jìn)行方法計(jì)時(shí):
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)點(diǎn):專業(yè)的性能指標(biāo)收集,可與Prometheus、Grafana等監(jiān)控系統(tǒng)集成,適合生產(chǎn)環(huán)境
缺點(diǎn):配置相對(duì)復(fù)雜,有一定學(xué)習(xí)成本
六、使用Filter實(shí)現(xiàn)請(qǐng)求耗時(shí)統(tǒng)計(jì)
創(chuàng)建一個(gè)Filter實(shí)現(xiàn)類,可以記錄每次HTTP請(qǐng)求的開始時(shí)間和結(jié)束時(shí)間,從而計(jì)算出請(qǐng)求的整體耗時(shí)。
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ù)處理請(qǐng)求
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
String requestUri = ((HttpServletRequest) request).getRequestURI();
System.out.println("請(qǐng)求[" + requestUri + "]耗時(shí):" + executionTime + "ms");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
優(yōu)點(diǎn):可以全局監(jiān)控所有Web請(qǐng)求的耗時(shí)。
缺點(diǎn):只提供整體請(qǐng)求的耗時(shí),無(wú)法深入到具體業(yè)務(wù)邏輯的執(zhí)行時(shí)間。
七、使用ServletRequestHandledEvent統(tǒng)計(jì)請(qǐng)求處理耗時(shí)
Spring Boot提供了ServletRequestHandledEvent事件,可以用來(lái)監(jiān)控HTTP請(qǐng)求的處理時(shí)間。這種方式適合于全局監(jiān)控所有的請(qǐng)求。
首先,創(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("請(qǐng)求[" + event.getRequestUrl() + "]耗時(shí):" + event.getProcessingTimeMillis() + "ms");
}
}
這種方法會(huì)自動(dòng)監(jiān)聽處理結(jié)果,不需要在每個(gè)Controller中進(jìn)行顯式的耗時(shí)統(tǒng)計(jì)。
優(yōu)點(diǎn):不需要修改現(xiàn)有代碼,監(jiān)控全局請(qǐng)求的耗時(shí)
缺點(diǎn):不支持自定義請(qǐng)求的粒度控制
總結(jié)與對(duì)比
在SpringBoot中,以上七種方法各有優(yōu)缺點(diǎn),可以根據(jù)不同的場(chǎng)景選擇合適的方案:
- StopWatch手動(dòng)統(tǒng)計(jì):適合臨時(shí)測(cè)試,快速實(shí)現(xiàn)
- 全局AOP:適合對(duì)整個(gè)服務(wù)層進(jìn)行性能監(jiān)控
- 自定義注解+AOP:適合精細(xì)化控制,只監(jiān)控關(guān)鍵方法
- 攔截器:適合Web接口監(jiān)控
- Actuator+Micrometer:適合生產(chǎn)環(huán)境,與專業(yè)監(jiān)控系統(tǒng)集成
- Filter:適合全局請(qǐng)求監(jiān)控,輕量級(jí)實(shí)現(xiàn)
- ServletRequestHandledEvent:全局監(jiān)控HTTP請(qǐng)求處理時(shí)間,不需改動(dòng)代碼
以上就是SpringBoot中統(tǒng)計(jì)方法耗時(shí)的七種實(shí)現(xiàn)方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot統(tǒng)計(jì)方法耗時(shí)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot統(tǒng)計(jì)接口請(qǐng)求耗時(shí)的方法詳解
- SpringBoot統(tǒng)計(jì)接口調(diào)用耗時(shí)的三種方式
- Springboot之如何統(tǒng)計(jì)代碼執(zhí)行耗時(shí)時(shí)間
- Spring?Boot源碼實(shí)現(xiàn)StopWatch優(yōu)雅統(tǒng)計(jì)耗時(shí)
- springboot基于過濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作
- SpringBoot中的7種耗時(shí)統(tǒng)計(jì)的實(shí)現(xiàn)方法與應(yīng)用場(chǎng)景
相關(guān)文章
Java數(shù)組隊(duì)列概念與用法實(shí)例分析
這篇文章主要介紹了Java數(shù)組隊(duì)列概念與用法,結(jié)合實(shí)例形式分析了Java數(shù)組隊(duì)列相關(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-03-03
Spring中allowedOriginPatterns和allowedOrigins方法有何不同詳解
這篇文章主要給大家介紹了關(guān)于Spring中allowedOriginPatterns和allowedOrigins方法有何不同,allowedOriginPatterns和allowedOrigins都是用來(lái)設(shè)置允許跨域請(qǐng)求的來(lái)源,需要的朋友可以參考下2023-10-10
Java藍(lán)橋杯實(shí)現(xiàn)線段和點(diǎn)
本文主要介紹Java藍(lán)橋杯實(shí)現(xiàn)線段和點(diǎn)的內(nèi)容,感興趣的小伙伴可以參考下文2021-08-08
Java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)過程解析
這篇文章主要介紹了Java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
mybatis中bind標(biāo)簽和concat的使用說明
這篇文章主要介紹了mybatis中bind標(biāo)簽和concat的使用說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-12-12

