如何为方法调用封装计时方法?有没有简便的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
相关产品推荐
相关产品推荐

