在java日志记录中,最标准且对业务代码侵入性最小的方法是使用aop(面向切面编程)结合拦截器或注解来自动记录方法耗时。具体做法是定义一个自定义注解(如@logtime),并在切面中通过proceedingjoinpoint.proceed()包裹目标方法的执行,计算执行前后的时间戳差值,最后将耗时和参数信息写入日志。这样做可以在不修改原有业务逻辑的前提下,统一收集所有标记方法的性能数据。

如何手动实现方法耗时的统计逻辑
如果不依赖Spring AOP等框架,开发者可以通过在方法内部直接记录开始和结束时间来手动实现耗时统计。核心思路是在方法入口处获取当前系统时间(毫秒级),在方法出口处再次获取时间并相减,同时需要处理异常以确保即使方法报错也能输出耗时。这种方式适合单点排查或简单脚本,但会导致代码冗余。

import java.time.Instant;
public class ManualTimer {
public static void main(String[] args) {
long start = System.currentTimeMillis();
try {
// 模拟业务逻辑
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
long end = System.currentTimeMillis();
System.out.println("Method took: " + (end - start) + " ms");
}
}
}
运行上述代码,控制台将输出类似“Method took: 100 ms”的结果,准确反映了Thread.sleep所消耗的时间。这种手动方式虽然直观,但在复杂项目中难以维护,且容易遗漏异常分支的日志记录。
AOP切面中如何正确捕获异常并记录耗时
在使用AOP进行日志记录时,一个关键问题是当被切面方法抛出异常时,如何确保耗时日志依然能被打印。正确的做法是在切面的@Around通知中使用try-finally块包裹proceed()调用,而不是try-catch。这样无论方法成功返回还是抛出异常,finally块中的计时和日志记录逻辑都会执行,从而保证数据的完整性。如果仅在try-catch中记录,异常发生时日志可能会丢失。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
// 执行目标方法
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + duration + "ms");
}
}
}
该切面配置了@Around通知,拦截com.example.service包下的所有方法。预期结果是,无论目标方法是否抛出异常,控制台都会打印出方法签名及对应的执行耗时。例如,若UserService.getUser执行耗时50ms,则输出“public User com.example.service.UserService.getUser(...) executed in 50ms”。这种模式确保了监控数据的可靠性,避免了因异常导致的监控盲区。”,
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











