本文目录导读:

- 基础方案:手动计算时间(最直接)
- AOP 方案:统一拦截(推荐)
- Spring Boot Actuator + Micrometer(生产环境推荐)
- 使用 Spring Cloud Sleuth(分布式追踪)
- 过滤器/拦截器方案(Web 层)
- 高级方案:慢查询日志与告警
- 总结与最佳实践
在Java开发中,统计接口耗时(API响应时间)是一个非常常见的需求,主要用于性能监控、问题排查和优化,以下是几种从简单到复杂的实现方案:
基础方案:手动计算时间(最直接)
1 使用 System.currentTimeMillis()
@GetMapping("/example")
public String example() {
long startTime = System.currentTimeMillis();
// 业务逻辑
// ...
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("接口耗时: " + duration + " ms");
return "success";
}
2 使用 StopWatch(Spring 提供)
import org.springframework.util.StopWatch;
@GetMapping("/stopwatch")
public String stopwatchExample() {
StopWatch stopWatch = new StopWatch("接口耗时统计");
stopWatch.start();
// 业务逻辑
// ...
stopWatch.stop();
log.info("接口耗时: {} ms", stopWatch.getTotalTimeMillis());
// 可以统计多个步骤
System.out.println(stopWatch.prettyPrint());
return "success";
}
AOP 方案:统一拦截(推荐)
使用 Spring AOP 或自定义注解,统一统计耗时,避免代码侵入。
1 创建自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeLog {
String value() default "";
}
2 编写 AOP 切面
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimeLogAspect {
private static final Logger log = LoggerFactory.getLogger(TimeLogAspect.class);
@Around("@annotation(timeLog)")
public Object around(ProceedingJoinPoint joinPoint, TimeLog timeLog) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("接口: {} 耗时: {} ms",
joinPoint.getSignature().toShortString(),
duration);
return result;
} catch (Throwable throwable) {
long duration = System.currentTimeMillis() - startTime;
log.error("接口: {} 执行异常,耗时: {} ms",
joinPoint.getSignature().toShortString(),
duration);
throw throwable;
}
}
}
3 使用注解
@RestController
public class DemoController {
@TimeLog("查询用户信息")
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
// 业务逻辑
return userService.findById(id);
}
}
4 更通用的切面(拦截所有 Controller)
@Aspect
@Component
public class ControllerTimeAspect {
@Around("execution(* com.example.controller.*.*(..))")
public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
// 获取请求路径(如果是 Spring MVC)
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String uri = request.getRequestURI();
String method = request.getMethod();
log.info("[{}] {} 耗时: {} ms", method, uri, duration);
}
return result;
}
}
Spring Boot Actuator + Micrometer(生产环境推荐)
用于生产环境的监控,配合 Prometheus + Grafana。
1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
2 配置
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: your-appname
3 自定义 Metrics(可选)
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
@Component
public class ApiMetricsCollector {
private final MeterRegistry meterRegistry;
public ApiMetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordTime(String apiName, long durationMs) {
meterRegistry.timer("api.response.time", "api", apiName)
.record(durationMs, TimeUnit.MILLISECONDS);
}
}
使用 Spring Cloud Sleuth(分布式追踪)
用于微服务场景,可以查看一次请求经过的所有服务的耗时。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
配置后,日志会自动包含 Trace ID 和 Span ID,配合 Zipkin 可以可视化查看调用链路。
过滤器/拦截器方案(Web 层)
1 实现 HandlerInterceptor
@Component
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
Long startTime = (Long) request.getAttribute("startTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
log.info("[{}] {} 耗时: {} ms",
request.getMethod(),
request.getRequestURI(),
duration);
}
}
}
2 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor)
.addPathPatterns("/**");
}
}
高级方案:慢查询日志与告警
结合 AOP 和告警逻辑,超过阈值时发送告警。
@Around("@annotation(timeLog)")
public Object around(ProceedingJoinPoint joinPoint, TimeLog timeLog) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
// 慢查询告警
if (duration > 1000) { // 超过1秒
log.warn("慢接口警告: {} 耗时 {} ms",
joinPoint.getSignature().toShortString(),
duration);
// 发送告警邮件/钉钉通知等
alertService.sendSlowApiAlert(joinPoint.getSignature().toShortString(), duration);
}
return result;
}
总结与最佳实践
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 手动计算 | 临时调试 | 简单直接 | 代码侵入强 |
| AOP 注解 | 日常开发 | 无侵入,统一管理 | 需要引入 AOP |
| Actuator + Micrometer | 生产监控 | 可视化,与监控系统集成 | 部署复杂 |
| 过滤器/拦截器 | Web 层统一 | 无需修改业务代码 | 无法统计 Service 层 |
| Sleuth | 微服务 | 全链路追踪 | 依赖 Zipkin |
推荐方案:
- 开发测试:使用 AOP + 自定义注解
- 生产环境:Actuator + Micrometer + Prometheus + Grafana
- 微服务:Sleuth + Zipkin
选择哪种方案取决于你的项目复杂度和监控需求,对于大多数中小项目,AOP 方案是最实用且侵入性最低的选择。