在jmockit中,你可以使用MockUp来创建一个“fake”的实例,对某个方法指定自己的实现,而不是调用实际的方法。

对于接口类型,需要这样调用:

@Mocked
private SomeInterface mockInstance;mockInstance = new MockUp<SomeInteface>() {...
}.getMockInstance();

这个倒没有什么古怪的。估计又是使用了java.reflect.Proxy。这个技巧在很多Java框架中用到,比如Spring AOP对于接口类型的实现,就是通过Proxy来混入拦截器实现的。

但是,对于其他类型的调用,就比较奇怪了:

@Mocked
private SomeProxy mockInstance;new MockUp<SomeProxy>() {@Mockpublic int doSth() {return 1;}
};mockInstance.doSth(); // return 1

new出来的对象,如果没有赋值给新的变量,应该是随着GC回收。可就是在我的眼皮底下,mockInstance就这样被掉包了。

Spring AOP中,对于非接口类型,是通过CGLIB魔改字节码来实现拦截器注入的。所以我估计这个也是一样的道理。不过令人想不通的是,jmockit到底是什么时候进行移花接木的?没看到注入的地方啊……

只能通过看代码来揭秘了。先从MockUp的构造函数开始吧。

// MockUp.java
@Nonnull
private Class<T> redefineClassOrImplementInterface(@Nonnull Class<T> classToMock)
{if (classToMock.isInterface()) {return createInstanceOfMockedImplementationClass(classToMock, mockedType);}Class<T> realClass = classToMock;if (isAbstract(classToMock.getModifiers())) {classToMock = new ConcreteSubclass<T>(classToMock).generateClass();}classesToRestore = redefineMethods(realClass, classToMock, mockedType);return classToMock;
}

对于非接口类型,调用了redefineMethods来定义一个仿版。顺着redefineMethods找下去,终于发现了jmockit的“作案手法”。

// MockClassSetup.java
@Nullable
private byte[] modifyRealClass(@Nonnull Class<?> classToModify)
{if (rcReader == null) {rcReader = createClassReaderForRealClass(classToModify);}MockupsModifier modifier = new MockupsModifier(rcReader, classToModify, mockUp, mockMethods);rcReader.accept(modifier, SKIP_FRAMES);return modifier.wasModified() ? modifier.toByteArray() : null;
}

看到byte[]的函数返回类型,估计就是在这里实现了字节码的转换,然后返回了新的被掉包的class文件了。沿着MockupsModifier看下去,可以看到jmockit是用ASM来改动原来的实现(具体见external.asm这个包,我就没有细看了)。

众所周知,Java代码先是编译成class文件,然后由JVM加载运行的。围绕JVM这一中间层,各种有趣的技术应运而生。比如各种类加载器,可以动态地去加载同名的类的不同实现(不同的class文件)。还有各种魔改class文件的手段,在原来的实现中注入自己的代码,像ASM、javassist、GCLIB,等等。jmockit就是应用ASM来修改原来的class文件,用mocked的实现掉包原来的代码。因为MockUp的构造已经触发了“狸猫换太子”的幕后行为,所以这里就不用把new出来的东西赋值给具体变量了。

还有一个问题。我们虽然弄明白了jmockit的作案手法,可是还没有找到掉包现场呢!即使现在jmockit已经持有了被篡改后的字节码,可它又是怎么替换呢?

继续看下去,发现jmockit把修改后的字节码存在StartUp.java里面了。转过去会看到,jmockit这里用到了JDK6的一个新特性:动态Instrumentation。怪不得jmockit要求JDK版本知识在6以上。

关于动态Instrumentation,具体可以看下这篇文章:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/
简单来说,通过这一机制可以实现监听JVM加载类的事件,并在此之前运行自己的挂钩方法。这么一来,掉包现场也找到了。

那jmockit怎么知道要监听哪些类呢?前面可以看到,需要Mock的类上,要添加Mocked注解。所以jmockit编写了一些跟主流测试框架集成的代码,在测试运行的时候获取带该注解的类。这样就知道要监听的目标了。

总结一下:jmockit先通过Mocked注解标记需要Mock掉的类。然后调用new MockUp去创建修改后的class文件。在JVM运行的时候,通过JDK6之后的动态Instrumentation特性监听类加载事件,并在目标类加载之前移花接木,用魔改后的字节码换掉真货。虽然Java是门静态类型语言,不过幸亏有字节码和JVM作为中间层,使得mock实现起来相对容易。

Jmock 原理简单说明相关推荐

  1. 【组合数学】鸽巢原理 ( 鸽巢原理简单形式示例 4、5 )

    文章目录 一.鸽巢原理简单形式示例 4 二.鸽巢原理简单形式示例 5 一.鸽巢原理简单形式示例 4 假设有 333 个 777 位二进制数 , A:a1a2a3a4a5a6a7A : a_1a_2a_ ...

  2. 【组合数学】鸽巢原理 ( 鸽巢原理简单形式 | 鸽巢原理简单形式示例 1、2、3 )

    文章目录 一.鸽巢原理简单形式 二.鸽巢原理简单形式示例 1 三.鸽巢原理简单形式示例 2 四.鸽巢原理简单形式示例 3 一.鸽巢原理简单形式 鸽巢原理 : 将 n+1n + 1n+1 个物体 放到 ...

  3. Nginx介绍及原理简单分析

    快速入门 ------------------------ 关于Nginx,我们可以到其官网 http://nginx.org/  以及WIKI http://wiki.nginx.org 进行下载和 ...

  4. BP神经网络原理简单介绍以及公式推导(矩阵形式和分量形式)

    BP神经网络原理简单介绍以及公式推导 标签(空格分隔): 神经网络 \def\net(#1){net^{(#1)}} \def\Y(#1){Y^{(#1)}} \def\part(#1){\parti ...

  5. 岭回归原理简单分析与理解

    岭回归原理简单分析与理解 首先说明一下,岭回归用于回归.学习岭回归,给我的感受是,它的数学公式推导比较简单,然而理解其含义相对难一些,所以本文着重引导你去理解它,而不仅仅是会推导公式.至于背景介绍,网 ...

  6. 邻近算法(KNN)原理简单解析

    邻近算法(KNN)原理简单解析 一.什么是邻近算法 1.1简介 1.2核心思想 1.3 算法流程 1.4 优缺点 二.实例演示KNN算法 一.什么是邻近算法 1.1简介 邻近算法,或者说K最近邻(KN ...

  7. 锐速与BBR的原理简单解析

    锐速与BBR的原理简单解析  4 前言 昨天,有一位朋友在我的文章下留言说,锐速和BBR不都是一样,是拥塞算法嘛.因为这方面需要讲的东西比较多,所以我还是专门水一篇文章吧. 锐速 参考资料: http ...

  8. 编译原理简单优先分析算法

    简单优先分析算法 编译原理简单优先分析算法是一种自下而上的分析算法,本例是编译原理第三版何炎祥中一个简单优先分析算法的例子,未给出分析过程分析代码,读者可以参考我的语法分析器中输出代码,写出此算法的输 ...

  9. TI CC1101学习笔记:工作原理简单入门

    众所周知,在IOT的市场应用中,从通信协议细分的话,有SUB-1G,2.4G,3G, 4G,以及最新推出的5G,虽然5G通信协议已经在崭露头角,但是不同的通信协议在不同的应用领域之中还是占据着一定的重 ...

最新文章

  1. [《孔雀》观后]聪明的孩子提着易碎的灯笼
  2. VB 禁止修改系统时间
  3. 计算机二级2021辅导书,2021该如何准备计算机二级考试?
  4. 软件工程概论 课堂练习 第3次作业3【画出“工资处理”子系统的结构图】
  5. 习题7 7-7 字符排队
  6. 获取系统特殊文件夹的路径
  7. 扩容原理_硬核丨一文读懂以太坊扩容方案 ─ Rollup 工作原理
  8. 清除服务器上传队列的文件,webUploader上传demo
  9. extjs 点击下拉框三角形触发事件_省市县三级联动下拉框的具体实现
  10. 新华三助力公安构建新IT“警盾”
  11. java设计模式----简单工厂
  12. python版本的适配器模式(对象)
  13. android 监听通知栏点击事件,android使用NotificationListenerService监听通知栏消息
  14. JLink下载Hex文件
  15. aria2linux安装教程,Ubuntu 安装下载工具 Aria2
  16. 三菱PLC基础知识 辅助继电器M
  17. 端端正正做人,踏踏实实做事
  18. moles-packer_Hashicorp的Packer-是否适合PHP开发人员?
  19. centos7 crontab未执行问题排查
  20. 基尔霍夫定理与电位计算

热门文章

  1. mysql如何获取句柄_关于获取进程句柄的问题
  2. 深度学习(十九)——FCN, SegNet, DeconvNet, DeepLab, ENet, GCN
  3. 可逆素数编程c语言,巧用C语言求四位的可逆素数
  4. PLSQL Developer中数值字段由科学计数法改为普通数值显示
  5. Oracle处理小数点后位数、格式化数字、查找指定字符所在位置的几个函数
  6. linux kill命令详解
  7. [转]写好shell脚本的13个技巧
  8. ASP.NET Core应用针对静态文件请求的处理[1]: 以Web的形式发布静态文件
  9. 【Android自定义View实战】之自定义评价打分控件RatingBar,可以自定义星星大小和间距...
  10. Gartner:2020年企业中无“云”战略将极为罕见