Object类方法需单独处理,因其在JDK动态代理中默认委托给Proxy实例而非目标对象,若纳入统计会污染监控数据;应在InvocationHandler.invoke()中通过method.getDeclaringClass() == Object.class判断并直接调用,跳过耗时统计逻辑。

Java 中利用 Object 类本身并不能直接实现方法耗时统计切面,真正起作用的是 动态代理机制(如 JDK Proxy 或 CGLIB)配合反射调用,而 Object 类的作用主要体现在:它是所有类的父类,代理对象最终也继承自它,且代理逻辑中常需调用 Object 的方法(如 toString()、hashCode()、equals())——这些方法在代理中需特殊处理,否则可能影响耗时统计的准确性。
为什么 Object 方法要单独处理?
在基于 InvocationHandler 的 JDK 动态代理中,若未显式拦截 Object 的公共方法(如 toString),它们会默认委托给代理对象自身(即 Proxy 实例),而非目标对象。这会导致:
- 统计到的是
Proxy.toString()的执行时间,而非业务方法 - 若误将
Object方法纳入耗时统计,会污染监控数据(比如高频调用toString()拉低平均值) - 某些框架(如 Spring AOP)默认不代理
Object方法,需手动识别并跳过
用 JDK Proxy 实现耗时统计切面(含 Object 方法过滤)
核心思路:在 invoke() 中判断方法是否属于 Object 类,是则直接调用,不走耗时统计逻辑。
示例代码:
立即学习“Java免费学习笔记(深入)”;
public class TimingInvocationHandler implements InvocationHandler {
private final Object target;
<pre class="brush:php;toolbar:false;">public TimingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ✅ 关键:跳过 Object 自身的方法,避免干扰统计
if (method.getDeclaringClass() == Object.class) {
return method.invoke(proxy, args);
}
long start = System.nanoTime();
try {
return method.invoke(target, args);
} finally {
long cost = System.nanoTime() - start;
System.out.printf("[Timing] %s.%s() took %.3f ms%n",
target.getClass().getSimpleName(),
method.getName(),
cost / 1_000_000.0);
}
}}
// 使用示例 Service service = new ServiceImpl(); Service proxy = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new TimingInvocationHandler(service) ); proxy.doWork(); // ✅ 统计 doWork() proxy.toString(); // ❌ 不统计,直接走 proxy.toString()
更实用的增强建议
-
按注解控制开关:只对加了
@Timed的方法统计,避免全局拦截带来的性能开销 - 集成日志或监控系统:把耗时结果发给 Micrometer、Prometheus 或写入 SLF4J MDC,而非仅打印
-
区分同步/异步方法:对返回
CompletableFuture的方法,需在回调中记录结束时间 -
CGLIB 场景注意:CGLIB 代理的是子类,
Object方法默认仍委托给父类(即目标对象),但需确保不重复代理(如 Spring 默认已处理)
Spring AOP 中的等效做法
Spring 默认不会代理 Object 方法,所以你写的切点表达式如 @Around("execution(* com.example..*.*(..))") 天然就避开了它们。但若手动使用 ReflectiveMethodInvocation 或自定义 Advisor,仍需检查 method.getDeclaringClass() == Object.class 并跳过。
不复杂但容易忽略 —— 真正让耗时统计可靠的关键,往往不是怎么记时间,而是清楚哪些方法不该记。


















