Java方法耗时统计推荐用System.nanoTime()手动计时或Spring AOP统一拦截;需覆盖异常路径、合理设置日志级别,避免在高频小方法中滥用。

在 Java 中,要在方法前后打印耗时统计,核心思路是:记录进入方法前的时间戳,方法执行完后再计算差值并输出。最常用、最轻量的方式是手动使用 System.nanoTime()(推荐)或 System.currentTimeMillis(),配合日志框架(如 SLF4J + Logback)打印。
手动添加时间统计(适合少量关键方法)
直接在方法开头和结尾插入计时逻辑,简单直接,无额外依赖:
- 用
System.nanoTime()更精确(纳秒级),适合统计方法内部耗时,不受系统时间调整影响 - 避免用
new Date()或Calendar,它们开销大且精度低 - 日志建议用占位符格式(如
log.debug("doWork finished, cost {} ms", costMs)),避免字符串拼接影响性能
示例:
public void doWork(String param) {
long start = System.nanoTime();
try {
// 实际业务逻辑
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("doWork interrupted", e);
return;
} finally {
long costNs = System.nanoTime() - start;
long costMs = TimeUnit.NANOSECONDS.toMillis(costNs);
log.info("doWork(param={}) executed, cost {} ms", param, costMs);
}
}
用 AOP 统一拦截(适合全量或按规则统计)
如果多个方法都需要耗时统计,重复写 try-finally 不现实。Spring AOP 是标准解法,通过切面自动织入计时逻辑:
立即学习“Java免费学习笔记(深入)”;
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 定义一个切点(如标注
@Timed的方法,或匹配特定包/命名规则) - 在环绕通知中获取开始时间、执行目标方法、计算耗时、记录日志
- 可结合 MDC 在日志中附加 traceId、method 等上下文,便于链路追踪
示例切面片段:
@Aspect
@Component
public class TimingAspect {
private static final Logger log = LoggerFactory.getLogger(TimingAspect.class);
<pre class='brush:java;toolbar:false;'>@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(com.example.annotation.Timed)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
String method = joinPoint.getSignature().toShortString();
try {
Object result = joinPoint.proceed();
long costNs = System.nanoTime() - start;
log.info("{} executed in {} ms", method, TimeUnit.NANOSECONDS.toMillis(costNs));
return result;
} catch (Throwable ex) {
long costNs = System.nanoTime() - start;
log.warn("{} failed after {} ms: {}", method, TimeUnit.NANOSECONDS.toMillis(costNs), ex.getMessage());
throw ex;
}
}}
用注解 + 自定义处理器(更灵活的声明式方案)
若不想强耦合 Spring AOP,或需支持非 Spring 环境,可自定义注解 + 运行时代理(如 ByteBuddy)或编译期注解处理器。但对多数项目,推荐基于 Spring 的 @Timed(Micrometer)或自定义 AOP 切面,平衡简洁性与可控性。
- Micrometer 的
@Timed可自动上报到 Prometheus,适合监控场景 - 自定义注解(如
@LogTime)配合 AOP,语义更清晰,也方便开关控制 - 注意:不要在高频调用的小方法上无差别加耗时日志,可能成为性能瓶颈
注意事项与避坑点
实际使用中容易忽略的关键细节:
-
异常路径必须覆盖:仅在方法末尾记时间会漏掉异常中断的情况,务必用
try-finally或 AOP 的catch+finally逻辑 -
避免日志级别误用:耗时日志建议用
INFO或DEBUG,勿用WARN或ERROR(除非超阈值告警) -
区分“耗时”和“等待”:如含 I/O、锁等待、线程阻塞,
nanoTime统计的是挂钟时间(wall-clock time),不是纯 CPU 时间 -
异步方法要小心:AOP 默认不拦截异步方法(
@Async),需配置 `expose-proxy=true` 或改用 AspectJ 编译织入


















