JMockit/Mockito/PowerMockit/Robo
目录
- 概念学习
- 代理模式
- mockito原理
- PowerMockito原理
- Robolectric 原理
- JMockit原理
- 多说几句
概念学习
要学习几个框架的原理,首先必须了解以下几个概念
代理模式
静态代理
动态代理
CGLIB
ASM
ByteBuddy
代理模式/静态代理/动态代理
代理模式就不多说了
https://www.runoob.com/design-pattern/proxy-pattern.html
这里讲的静态代理,就是代理模式的最基本的一种实现方式,
在开发者编程的时候定义好接口interface,也写好 该接口的实现类proxy class(代理类),
这种就是叫静态代理。
动态代理的意思就是,这个interface已经定义好了,但是实现类,是在运行时,通过某种方式在内存里创建的。
通过java提供的
Proxy类,InvocationHandler接口等api完成动态生成。
底层实现是,通过指定的classLoader,利用反射,完成代理类proxy class的生成,具体怎么生成的可自行跟踪
关键代码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
参考链接
https://blog.csdn.net/riemann_/article/details/86777505
CGLIB/ASM
其实cglib跟以上的静态动态代理不同,cglib是一个库,后者是一个理念(具体实现是反射创建类),同理ASM也只是一个库,这里能放在一起比较,只是单纯从代理的角度去做比较。
cglib提供Enhance等api,将真实对象类的class文件加载进来,通过ASM修改字节码生成其子类,覆盖父类相应的方法(真正在修改字节码的是ASM库)
CGLIB和Java动态代理的区别
-
Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承),CGLIB则可以要求实现类不用实现任何接口。CGLIB动态生成的代理类会继承我们的业务类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)
-
Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
可以参考
https://zhuanlan.zhihu.com/p/35144462
看下cglib生成一个代理类的过程
参考链接
CGLIB动态代理实现与原理
mockito原理
一句话『java动态代理+byteBuddy生成字节码』
mock对象的创建主要是为了创建一个mock对象的代理类,使得代理类代理mock对象的所有方法(从源码设计中包含了私有方法,但是受私有方法封装性原因,该私有方法无法进行mock).
mock实例方法的场景利用了Java运行时多态的原理,通过重写父类的方法来修改某个方法的行为。具体细节:
mockito首先接受一个指定的class类型,采用java的动态代理逻辑,使用了ByteBuddy框架来生成该class类型的代理实例。(ByteBuddy这个框架,它并不需要编译器的帮助,而是直接生成class,然后使用ClassLoader来进行加载)
代理对象继承了被mock类,为了对每个方法做拦截,通过byte buddy设置拦截器 MockMethodInterceptor。MockMethodInterceptor的主要作用是将mock对象的拦截方法执行交给了MockHandler来处理。
具体代码跟踪
https://albenw.github.io/posts/9758301d/
后续的关键点就在这个ByteBuddy工具怎么生成动态类对象了。
PowerMockito原理
动态代理+ javassist修改字节码+ objenesis绕过构造方法来实例化对象
PowerMockito比Mockito强大的地方在
- 当某个测试方法被注解@PrepareForTest标注以后,在运行测试用例时,会创建一个新的org.powermock.core.classloader.MockClassLoader实例,然后加载该测试用例使用到的类(系统类除外)。
- PowerMock会根据你的mock要求,去修改写在注解@PrepareForTest里的class文件(当前测试类会自动加入注解中),以满足特殊的mock需求。例如:去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等。
- 如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。
参考链接
https://www.cnblogs.com/zjdxr-up/p/11571207.html
Robolectric 原理
ASM 修改字节码 + classLoader替换
https://www.jianshu.com/p/0848a47d37ab
JMockit原理
先使用asm修改原有class的字节码, 再利用jdk的instrument机制替换现有class的内容(使用的工具是agent)
从而达到mock的目的。
整个mock过程看
参考链接
https://zhuanlan.zhihu.com/p/106882864
对比
版本都与现在不一样了。
按高票的意思是,JMockit更加灵活。