You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为方法调用封装计时方法?有没有简便的StopWatch封装方式?

方法调用的计时封装方案

针对你现在重复写StopWatch的start/stop和日志的问题,有几种简便的封装方式,能大幅减少重复代码:

1. 通用工具类静态方法(最直接)

封装一个工具类,把计时逻辑抽出来,支持无返回值和有返回值的方法调用:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch; // 或者你使用的其他StopWatch实现
import java.util.function.Supplier;

public class StopWatchUtils {
    private static final Logger log = LoggerFactory.getLogger(StopWatchUtils.class);

    // 无返回值方法的计时
    public static void timed(String operationDesc, Runnable task) {
        StopWatch sw = new StopWatch();
        sw.start();
        try {
            task.run();
        } finally {
            sw.stop();
            log.trace("{} took {} msecs", operationDesc, sw.getTotalTimeMillis());
        }
    }

    // 有返回值方法的计时
    public static <T> T timed(String operationDesc, Supplier<T> task) {
        StopWatch sw = new StopWatch();
        sw.start();
        try {
            return task.get();
        } finally {
            sw.stop();
            log.trace("{} took {} msecs", operationDesc, sw.getTotalTimeMillis());
        }
    }
}

然后你的原代码可以简化成:

for (Foo foo : foos) {
    StopWatchUtils.timed("saved foo", () -> someService.processFoo(foo));
}

如果processFoo有返回值,就用带Supplier的重载:

Foo result = StopWatchUtils.timed("saved foo", () -> someService.processFoo(foo));

2. AOP注解方式(适合大量方法需要计时)

如果你的项目用Spring,可以通过自定义注解+切面实现自动计时,完全不用在业务代码里写计时逻辑:

第一步:定义@Timed注解

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
    // 自定义操作描述,默认用方法名
    String value() default "";
}

第二步:编写切面类

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;
import org.springframework.util.StringUtils;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class TimedAspect {
    private static final Logger log = LoggerFactory.getLogger(TimedAspect.class);

    @Around("@annotation(timed)")
    public Object handleTiming(ProceedingJoinPoint joinPoint, Timed timed) throws Throwable {
        // 生成操作描述
        String operationDesc = StringUtils.hasText(timed.value()) 
            ? timed.value() 
            : joinPoint.getSignature().getName();
        
        StopWatch sw = new StopWatch();
        sw.start();
        try {
            // 执行原方法
            return joinPoint.proceed();
        } finally {
            sw.stop();
            log.trace("{} took {} msecs", operationDesc, sw.getTotalTimeMillis());
        }
    }
}

第三步:给目标方法加注解

在someService.processFoo方法上添加注解:

@Timed("saved foo")
public void processFoo(Foo foo) {
    // 原方法逻辑
}

之后业务代码里直接调用就行,计时逻辑完全由切面处理:

for (Foo foo : foos) {
    someService.processFoo(foo);
}

注意点

  • 用finally块确保StopWatch一定会被stop,避免方法抛出异常导致计时不完整
  • 如果用的不是Spring的StopWatch,换成你当前使用的实现类即可
  • 日志用trace级别的话,要确保日志配置中开启了对应类的trace输出

内容的提问来源于stack exchange,提问作者user352290

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:30:29