SpringBoot实战:七种统计方法耗时的实现方式

springboot学习(七十四) spring中时钟计时器StopWatch的使用 StopWatch时钟计时器 阅读详情

作为开发者,我们经常需要统计方法的执行时间,以便找出性能瓶颈,优化系统响应速度。今天分享在SpringBoot框架中实现方法耗时统计的几种方法。

一、手动使用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中,以上七种方法各有优缺点,可以根据不同的场景选择合适的方案:

  1. StopWatch手动统计:适合临时测试,快速实现
  2. 全局AOP:适合对整个服务层进行性能监控
  3. 自定义注解+AOP:适合精细化控制,只监控关键方法
  4. 拦截器:适合Web接口监控
  5. Actuator+Micrometer:适合生产环境,与专业监控系统集成
  6. Filter:适合全局请求监控,轻量级实现
  7. ServletRequestHandledEvent:全局监控HTTP请求处理时间,不需改动代码
java-使用Arthas全链路统计方法调用链和执行耗时 Arthas监控诊断产品功能比较多,可以帮助我们不停服务在线诊断问题。使用jmh配合Arthas可以进行吞吐量、平均执行时间等基础测试和性能监控分析,帮助开发人员科学定位性能瓶颈和优化点。 阅读详情

相关推荐

战损版JavaAgent方法耗时统计工具实现

本篇文章将实现一个超绝战损版的基于Java Agent的方法耗时统计工具。Java Agent原理简析;方法耗时统计工具实现;方法耗时工具的Springboot的starter包实现。Java Agent就是一个无法独立运行的jar包,其加载时机可以是程序运行前和程序运行中,也就是基于Java Agent可以实现在程序运行前和程序运行中来动态的修改类。方法耗时统计,简单的思路就是使用切面去切,首先想到的就是使用Spring的AOP来切,但是Spring的AOP都知道是基于动态代理,但是无论是JDK。

weixin_74412978的博客 898

SpringBoot --- StopWatch (计时器)

StopWatch 是 org.springframework.util包下面的一个工具类,通过这个可以对程序部分代码进行计时。适用于同步单线程代码块

yuanchenglong的博客 3192

java进阶|基于springAop和自定义注解进行方法的耗时统计

本想着基于aop做些比较实际一点的业务场景,但是服务器不给力,暂时没法连接上了,额,这也是自己没有去写其它内容的客观原因之一了,因为涉及不了数据库层面,所以慢慢拖到了现在,也...

qq_35006660的博客 1240

Spring Boot学习|Stopwatch 在 Spring Boot 中的使用

`Stopwatch` 是由 Apache Commons Lang 库提供的一种工具类,它允许你测量经过的时间。它可以用来追踪代码中某部分、方法或应用内任何流程的执行时间。它特别适用于性能分析和优化。

weixin_44435110的博客 3029

Springboot 统计 代码执行耗时时间 ,玩法多到眼花

Springboot 统计 代码执行耗时时间 ,玩法多到眼花

默默不代表沉默 8066

SpringBoot开发——统计接口调用耗时的几种方法

SpringBoot开发——统计接口调用耗时的几种方法

bjzhang75的博客 2488

自定义注解实现统计方法耗时

使用springaop实现自定义注解统计方法耗时

zxwyhzy的博客 1724

SAP PCE私有云MM02修改物料主数据基本单位报错

当前发现只有改为AAAADL(无量纲)单位时会报错,其他的正常。一开始以为配置的ISO码换算有问题,对配置排查后,排除配置问题。最后发现是PCE当前版本的原因,需要打上NOTE。SAP PCE私有云在没有库存和业务单据的情况下,MM02修改物料主数据基本单位报错“EA CPZP MK000000000000:PCS 不可转换为”,详见截图。点击【显示错误】,提示EA CPZP MK000000000000:PCS 不可转换为。从SAP FOR ME下载NOTE 3531359,链接。

dingqing86的专栏 519

太全了!Spring Boot 3.4 七种方法耗时统计实现,一文搞定!

手动记录,到高级的 AOP 切面、再到生产级的 Micrometer + Actuator 监控体系,全方位覆盖应用开发中的各类场景,助你轻松搞定耗时统计,提升系统稳定性与可维护性!在日常开发过程中,我们经常会遇到系统响应变慢、页面加载迟缓、接口卡顿等问题,而这些问题背后隐藏的往往是。如果你只想统计某些特定方法的耗时,又不想在方法里写逻辑,推荐这种方式。如果你只关心 Web 层接口的耗时统计,用拦截器最合适不过了。无论是对接口响应的监控、还是对业务逻辑执行效率的衡量,

xiexiaoming052的博客 496

蜗牛星际+OpenWrt实战:双网口变AP模式,让所有设备同网段互通

本文详细介绍了如何将蜗牛星际双网口小主机刷入OpenWrt系统,并将其配置为AP模式,实现与主路由同网段的无缝桥接。通过关闭DHCP、桥接物理网口等核心步骤,解决家庭网络设备割裂问题,让所有设备在同一局域网内高效互通,特别适合追求网络简洁和厌恶多级NAT的技术爱好者。

zero1的博客 962

剪映自动化工具来了:AI帮你自动剪辑成片

🎬分享一个剪映自动化剪辑工具!AI 自动帮你完成从写文案、配音、加字幕、选音乐、上特效到最终导出的整套流程🎈

XiaoqiangClub的博客 856

SpringBoot中使用AOP实现耗时统计

Spring中一个重要的点就是面向切面编程,即AOP,可以实现程序中功能的解耦,让一些类共享相同的行为动作。Spring中AOP的实现主要通过JDK的动态代理和CGLIB实现。 一、AOP中相关术语 1、切面(Aspect): 是指横切多个对象的关注点的一个模块化,切面通过常规类(基本模式方法)或者通过使用了注解@Aspect的常规类来实现。 2、连接点(Joint point): 就是spring中允许使用通知的地方,基本上每个方法前后抛异常时都可以是连接点 3、通知(Advi

有问题请发邮箱dengyifanlittle@163.com进行讨论 3757

基于 STM32 的全自动洗车监控系统设计与实现

本文提出一种基于 STM32F103RCT6 芯片的全自动洗车监控系统方案,通过多传感器融合与智能控制算法,实现车辆检测、洗车流程自动化及状态远程监控。系统集成硬件选型、电路设计、软件流程及通信功能,可广泛应用于智能洗车场景。

weixin_42300449的博客 964

javaagent统计方法执行耗时

新建一个maven工程,命名为javaagent,其中pom.xml内容如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org

juong的博客 910

声音源定位:利用YOLO与麦克风阵列定位声音来源YOLOv5、YOLOv8、YOLOv10

YOLO(You Only Look Once)是一个用于目标检测的深度学习模型。YOLO的最大特点是其通过一次前向传播即可完成多个目标的检测,速度和精度相对其他目标检测算法具有显著优势。YOLO模型的版本不断更新,从YOLOv1到YOLOv10,算法精度和速度都有了显著的提升。声音源定位是利用多个麦克风阵列收集的声音信号,通过算法计算声音源的位置。声音源定位的关键在于准确估计每个麦克风的声音到达时间,从而确定声音源的空间坐标。基于时延估计(TDOA)

YOLO 673

实战 | OpenCV实现扫描文本矫正应用与实现详解(附源码)

导读 本文主要介绍使用OpenCV对扫描文本矫正的应用实例及详细实现步骤。 背景介绍 在使用打印机或扫描仪扫描文档时,由于摆放位置差异难免造成扫描文档的倾斜。本文将使用OpenCV将倾斜的文档矫正水平并去除黑边。 实现步骤 本文只针对包含大部分文字的文档做倾斜矫正,思路来源是大部分文字倾斜后同一行的也在同一条直线,通过拟合直线计算文本倾斜角度,然后旋转来实现文本倾斜矫正。 详细步骤: ①加载倾斜图像 ②灰度转换+腐蚀+膨胀 ③Canny边缘检测 + 霍夫线变换检测直线 ...

Color Space的博客 3400

「字节码插桩」统计方法耗时(第一篇:初出茅庐)- 第311篇

师傅:徒儿,听过字节码插桩嘛? 徒儿:师傅,徒儿诗如李白,貌如潘安,字如王羲之,歌如刘德华,智慧如诸葛亮,数学如华罗庚,对于字节码插桩,我怎么能不懂呐。 师傅:徒儿,你这什么时候,练就了吹牛逼吹的都不脸红的境界了,来来来,你倒是和为师说说什么是字节码插桩。 徒儿:字节码插桩就是这么一个意思,字节码… 插… 桩… ,师傅,你品,你品,你细品。 师傅:我品啥?你貌似什么都没有说嘛。还诗如李白,智慧如诸葛亮呐,我看你是吹牛逼的本身涨了不少呐。 徒儿:我这牺牲小我,开心大家的吗,师傅,你看我.

悟纤学院 2万+

华为OD面试手撕真题 【合并相邻且相等的元素】多语言题解

华为OD面试高频手撕真题【合并相邻且相等的元素】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解。

qq_45776114的博客 549

大爱这个IDEA插件,不写代码照样统计方法耗时

???? 这是一个或许对你有用的社群???? 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:《项目实战(视频)》:从书中学,往事上“练”《互联网高频面试题》:面朝简历学习,春暖花开《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题《精进 Java 学习指南》:系统学习,互联网主流技术栈《必读 Java 源码专栏》:知其然,知其所以然????这是一个或许...

芋艿V 146

selenium绕过检测,规避检测

现在不少大网站有对selenium采取了监测机制。比如正常情况下我们用浏览器访问淘宝等网站的 window.navigator.webdriver的值为 undefined或者为false。而使用selenium访问则该值为true。那么如何解决这个问题呢?我们正常浏览器登录查看,window.navigator.webdriver的值为false我们通过selenium打开的网页,测试,可见window.navigator.webdriver的值为true。

景天科技苑 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

SpringBoot电脑商城-创建订单以及统计方法耗时(AOP)

创建订单订单创建数据库表创建实体类持久层业务层控制层前端页面统计业务方法耗时功能AOP切面方法统计业务方法执行时长 订单 点击在线支付,可以将该订单添加到数据库中。 创建数据库表 CREATE TABLE t_order ( oid INT AUTO_INCREMENT COMMENT '订单id', uid INT NOT NULL COMMENT '用户id', recv_name VARCHAR(20) NOT NULL COMMENT '收货人姓名', recv_phone VARCHAR

weixin_45581692的博客 1868

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

上一篇: Spring Boot 的 20个实用技巧
下一篇: Spring Boot 数据脱敏实现
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值