一、手动使用stopwatch
最直接的方法是使用spring提供的stopwatch类,这种方式简单直观,适合临时性的性能测试。
import org.springframework.util.stopwatch;
@service
public class userservice {
public user finduserbyid(long id) {
stopwatch stopwatch = new stopwatch();
stopwatch.start();
// 业务逻辑
user user = userrepository.findbyid(id).orelse(null);
stopwatch.stop();
system.out.println("finduserbyid方法耗时:" + stopwatch.gettotaltimemillis() + "ms");
return user;
}
}
优点:简单直观,无需额外配置
缺点:侵入业务代码,不够优雅,需要手动添加到每个需要监控的方法
二、使用aop实现全局方法耗时统计
aop(面向切面编程)是实现方法耗时统计的理想选择,它可以在不修改原有代码的情况下,统一处理耗时统计逻辑。
首先,添加aop依赖:
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-aop</artifactid>
</dependency>
然后,创建切面类:
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();
// 执行目标方法
object result = joinpoint.proceed();
stopwatch.stop();
string methodname = joinpoint.getsignature().getname();
system.out.println("方法[" + methodname + "]耗时:" + stopwatch.gettotaltimemillis() + "ms");
return result;
}
}
优点:代码无侵入,统一管理,配置灵活
缺点:对于特定方法的定制化需求不够灵活
三、自定义注解+aop实现更精细的控制
这种方法结合了自定义注解和aop,可以更精确地控制哪些方法需要进行耗时统计。
首先,创建自定义注解:
import java.lang.annotation.*;
@target(elementtype.method)
@retention(retentionpolicy.runtime)
@documented
public @interface timelog {
string value() default "";
}
然后,创建切面类处理带有该注解的方法:
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) {
// 业务逻辑
return productrepository.findbyid(id).orelse(null);
}
}
优点:更精细的控制,注解可携带更多信息,便于定制
缺点:需要手动在方法上添加注解
四、使用拦截器统计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/");
}
}
优点:专注于web接口性能,对接口进行统一监控
缺点:只能监控controller层方法,无法监控内部服务方法
五、使用actuator + micrometer实现细粒度监控
spring boot actuator提供了与micrometer的集成,可以实现更专业的性能指标收集:
添加依赖:
<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);
// 业务逻辑
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
优点:专业的性能指标收集,可与prometheus、grafana等监控系统集成,适合生产环境
缺点:配置相对复杂,有一定学习成本
六、使用filter实现请求耗时统计
创建一个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();
// 继续处理请求
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() {}
}
优点:可以全局监控所有web请求的耗时。
缺点:只提供整体请求的耗时,无法深入到具体业务逻辑的执行时间。
七、使用servletrequesthandledevent统计请求处理耗时
spring boot提供了servletrequesthandledevent事件,可以用来监控http请求的处理时间。这种方式适合于全局监控所有的请求。
首先,创建事件监听器:
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");
}
}
这种方法会自动监听处理结果,不需要在每个controller中进行显式的耗时统计。
优点:不需要修改现有代码,监控全局请求的耗时
缺点:不支持自定义请求的粒度控制
总结与对比
在springboot中,以上七种方法各有优缺点,可以根据不同的场景选择合适的方案:
- stopwatch手动统计:适合临时测试,快速实现
- 全局aop:适合对整个服务层进行性能监控
- 自定义注解+aop:适合精细化控制,只监控关键方法
- 拦截器:适合web接口监控
- actuator+micrometer:适合生产环境,与专业监控系统集成
- filter:适合全局请求监控,轻量级实现
- servletrequesthandledevent:全局监控http请求处理时间,不需改动代码
以上就是springboot中统计方法耗时的七种实现方式小结的详细内容,更多关于springboot统计方法耗时的资料请关注代码网其它相关文章!
发表评论