SpringBoot中的7种耗时统计的实现方法与应用场景

 更新时间:2025年09月26日 08:37:01   作者:刘大华  
在日常开发中,经常会遇到一些性能问题,这篇文章主要介绍了SpringBoot中的7种耗时统计的实现方法与应用场景,大家可以根据需要进行选择

前言

在日常开发中,经常会遇到一些性能问题。

比如用户反馈:“这个页面加载好慢啊!” 这个时候,你该怎么办?

首先就得找出到底是哪个方法、哪段代码执行时间过长。

只有找到了瓶颈,才能对症下药进行优化。所以说,方法耗时统计是性能优化中非常重要的一环。

接下来,我就给大家介绍七种实用的实现方式,从简单到复杂,总有一种适合你!

1. System.currentTimeMillis()

这是最原始但最直接的方式,适用于快速验证某段代码的执行时间。

public void doSomething() {
    long start = System.currentTimeMillis();

    // 模拟业务逻辑
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

    long end = System.currentTimeMillis();
    System.out.println("方法执行耗时:" + (end - start) + "ms");
}

优点

  • 无需引入任何依赖
  • 简单直观,适合临时调试

缺点

  • 代码侵入性强
  • 多处使用时重复代码多
  • 精度受系统时钟影响(可能受NTP调整干扰)

适用场景

  • 本地开发调试
  • 快速验证某段逻辑耗时

注意:该方法基于系统时间,不适用于高精度计时。推荐使用 System.nanoTime() 替代(见后文补充)。

2. 使用StopWatch工具类

Spring 提供了org.springframework.util.StopWatch类,支持分段计时和格式化输出,适合需要统计多个子任务耗时的场景。

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%  更新缓存

优点

  • 支持多任务分段计时
  • 输出美观,便于分析
  • 可命名任务,提升可读性

缺点

  • 仍需手动插入代码
  • 不适用于自动化监控

适用场景

需要分析多个步骤耗时占比的复杂流程

3. 使用AOP切面+自定义注解(推荐)

通过面向切面编程(AOP),可以实现对指定方法的无侵入式耗时监控。

第一步:定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogCostTime {
    String value() default ""; // 方法描述
    long threshold() default 0; // 耗时阈值(ms),超过则告警
}

第二步:编写切面

@Aspect
@Component
@Slf4j
@Order(1) // 确保优先级
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);

            // 根据阈值决定日志级别
            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 默认支持;否则需添加 @EnableAspectJAutoProxy

第三步:使用注解

@Service
public class UserService {

    @LogCostTime(value = "根据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(根据ID查询用户) 耗时超阈值: 102 ms (阈值: 50 ms)

优点

  • 低侵入:只需添加注解
  • 可复用:一处定义,多处使用
  • 可扩展:支持阈值告警、慢查询监控等

适用场景

  • 核心服务方法
  • 远程调用(RPC/HTTP)
  • 数据库查询
  • 复杂计算逻辑

4. 使用Micrometer@Timed注解

Micrometer是现代Java应用的事实标准指标收集库,与Spring Boot Actuator深度集成,支持对接 Prometheus、Grafana、Datadog 等监控系统。

添加依赖

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

启用指标端点

management:
  endpoints:
    web:
      exposure:
        include: metrics, prometheus
  metrics:
    export:
      prometheus:
        enabled: true

使用@Timed注解

@Service
public class BusinessService {

    @Timed(
        value = "business.process.time",
        description = "业务处理耗时",
        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

优点

  • 标准化指标,支持多维度聚合
  • 可视化展示(Grafana)
  • 支持报警(Prometheus Alertmanager)

适用场景

  • 生产环境性能监控
  • 微服务架构下的统一指标体系

5. 使用Java8的Instant与Duration

Java 8 引入了新的时间 API,更加安全和易用。

public void doSomething() {
    Instant start = Instant.now();

    // 业务逻辑
    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());
}

优点

  • 使用现代时间 API,语义清晰
  • 线程安全,避免旧 Date 的坑

缺点

  • 仍需手动编码
  • 性能略低于 nanoTime

适用场景

偏好 Java 8+ 新特性的项目

6. 异步方法耗时统计CompletableFuture

对于异步任务,可通过回调机制统计耗时。

public CompletableFuture<Void> asyncProcess() {
    long start = System.nanoTime();

    return CompletableFuture.runAsync(() -> {
        // 模拟异步任务
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).whenComplete((result, ex) -> {
        long cost = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
        log.info("异步任务耗时:{} ms", cost);
    });
}

优点

  • 适用于非阻塞场景
  • 可结合线程池监控

适用场景

  • 异步消息处理
  • 批量任务调度

7. 使用HandlerInterceptor统计 Web 请求耗时

在 Web 层通过拦截器统一记录所有 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

优点

  • 全局覆盖所有请求
  • 无需修改业务代码

适用场景

  • Web 应用整体性能监控
  • API 网关层耗时分析

总结

方案侵入性适用场景是否推荐
System.currentTimeMillis()临时调试⚠️ 仅调试
StopWatch分段计时分析
AOP + 自定义注解核心方法监控✅✅✅ 强烈推荐
Micrometer @Timed生产监控集成✅✅✅ 生产首选
Instant + Duration现代化时间处理
CompletableFuture 回调异步任务
HandlerInterceptorWeb 请求全局监控✅✅

到此这篇关于SpringBoot中的7种耗时统计的实现方法与应用场景的文章就介绍到这了,更多相关SpringBoot统计耗时内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现图片文件上传

    Java实现图片文件上传

    这篇文章主要为大家详细介绍了Java实现图片文件上传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • SpringMVC Hibernate与JPA使用教程

    SpringMVC Hibernate与JPA使用教程

    这篇文章主要介绍了SpringMVC Hibernate与JPA,在正式进入Hibernate的高级应用之前,需要了解声明是数据模型与领域模型,这两个概念将会帮助我们更好的理解实体对象的关联关系映射
    2022-12-12
  • Springboot自动扫描包路径来龙去脉示例详解

    Springboot自动扫描包路径来龙去脉示例详解

    这篇文章主要介绍了Springboot自动扫描包路径来龙去脉示例详解,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • vue+springboot+webtrc+websocket实现双人音视频通话会议(最新推荐)

    vue+springboot+webtrc+websocket实现双人音视频通话会议(最新推荐)

    这篇文章主要介绍了vue+springboot+webtrc+websocket实现双人音视频通话会议,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-05-05
  • 如何使用HttpClient发送java对象到服务器

    如何使用HttpClient发送java对象到服务器

    这篇文章主要介绍了如何使用HttpClient发送java对象到服务器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 基于Java SWFTools实现把pdf转成swf

    基于Java SWFTools实现把pdf转成swf

    这篇文章主要介绍了基于Java SWFTools实现把pdf转成swf,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • windows java -jar无法启动jar包简单的解决方法

    windows java -jar无法启动jar包简单的解决方法

    这篇文章主要介绍了windows java -jar无法启动jar包简单的解决方法,文中通过代码介绍的非常详细,对大家学习或者使用java具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-12-12
  • JavaWeb文件上传入门教程

    JavaWeb文件上传入门教程

    这篇文章主要为大家详细介绍了JavaWeb文件上传入门教程,分析了文件上传原理、介绍了第三方上传组件,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Spring Cloud服务入口Gateway的介绍和使用问题小结

    Spring Cloud服务入口Gateway的介绍和使用问题小结

    Spring Cloud Gateway是Spring Cloud的⼀个全新的API⽹关项⽬, 基于Spring + SpringBoot等技术开发, ⽬的是为了替换掉Zuul,这篇文章主要介绍了Spring Cloud服务入口Gateway的介绍和使用问题小结,需要的朋友可以参考下
    2025-03-03
  • IDEA启动Springboot报错:无效的目标发行版:17 的解决办法

    IDEA启动Springboot报错:无效的目标发行版:17 的解决办法

    这篇文章主要给大家介绍了IDEA启动Springboot报错:无效的目标发行版:17 的解决办法,文中通过代码示例和图文讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-02-02

最新评论