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

Mockito如何在不调用构造方法的情况下实例化类?

Mockito无构造方法调用实例化的核心机制

1. 内联Mock的字节码修改逻辑

Mockito当前默认采用内联Mock生成器(InlineByteBuddyMockMaker),而非传统的子类代理模式:

  • 它会生成一个与目标类同名的动态修改类,因此mock.getClass()会返回原类的全限定名,但实际是字节码被修改后的类实例
  • 动态替换目标类的所有非final方法实现(比如你调试时看到的自定义toString(),格式为MyClass mock, hascode 123456,就是Mockito动态生成的)
  • 核心关键:直接绕过目标类的构造方法调用,跳过原构造的执行逻辑

2. 实例化的底层:MethodHandle与JVM原生支持

你调试到的MethodHandle.invokeExact()是实现构造绕过的核心,这是JVM提供的本地方法,直接操作对象创建的底层流程:

  • Mockito通过MethodHandles.Lookup获取构造方法的句柄,但并非直接调用构造方法
  • 它利用JVM对象分配与构造分离的特性:先通过底层API分配对象内存(类似Unsafe.allocateInstance但更合规),完全跳过原构造方法的执行,直接初始化Mockito代理所需的字段和逻辑
  • 你看到newInstance(type)未抛出异常,正是因为走了这条绕过构造的路径,没有触发目标类构造方法里的UnsupportedOperationException

3. 为什么没用到Objenesis?

Objenesis是旧版Mockito用于绕过构造的第三方工具,新版Mockito通过内联Mock+MethodHandle的组合,直接借助JVM合法API实现构造绕过,无需依赖Objenesis:

  • 只有当内联方式失败时(比如特殊类加载器、模块化环境限制等场景),才会降级到Objenesis或Unsafe方案
  • 你的调试流程中newInstance(type)成功执行,说明当前环境支持内联构造绕过逻辑

4. 字节码修改的具体操作细节

Mockito借助ByteBuddy完成的字节码修改包括:

  • 为目标类添加Mockito代理相关的字段,用于存储mock的行为配置、调用记录等
  • 重写所有非final方法,替换为Mockito的代理逻辑(比如方法调用时优先触发预定义的stub逻辑)
  • 修改类的初始化流程,跳过原构造方法的执行步骤,直接完成代理实例的初始化

针对你疑问的逐一解答

  • 为什么getClass()返回原类名?:内联Mock生成的动态类与原类同名,类加载器会加载这个修改后的类,因此返回的类名与原类一致
  • 原构造方法还存在吗?:原构造方法依然存在,但Mockito在实例化时完全跳过了它的调用,没有执行构造方法内的代码
  • Mock到底实例化的是什么?:实例化的是字节码被修改后的原类变体,继承关系并未改变,只是类的字节码被动态修改添加了代理逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:55:59