1、引子
在某天与QA同学进行沟通时,发现QA同学有针对某个方法调用时,有让该方法停止一段时间的需求,我对这部分的功能实现非常好奇,因此决定对原理进行一些深入的了解,力争找到一种使用者尽可能少的对原有代码进行修改的方式,以达到对应的MOCK要求。
整体的感知程度可以分为三个级别:
硬编码
增加配置
无需任何修改
2、思路
在对方法进行mock,暂停以及异常模拟,在不知道其原理的情况下,进行猜想,思考其具体的实现原理,整体来说,最简单的实现模型无外乎两种:
2.1 朴素思路
假设存在如下的函数
publicObject targetMethod(){
System.out.println(“运行”);
}
若想要在函数执行后暂停一段时间、返回特定mock值或抛出特定异常,那么可以考虑修改对应的函数内容:
public Object targetMethod(){
//在此处加入Sleep return 或 throw逻辑
System.out.println(“运行”);
}
或使用类似代理的方法把对应的函数进行代理:
public Object proxy(){
//执行Sleep return 或 throw逻辑
return targetMethod();
}
public Object targetMethod(){
System.out.println(“运行”);
}
2.2 略成熟思路
在朴素思路的基础上,我们可以看出,实现类似的暂停、mock和异常功能整体实现方案无外乎两种:
代理模式
深入修改内部函数
在这两种思路的基础上,我们从代理模式开始考虑(主要是代理使用的比较多,更熟悉)
2.2.1 动态代理
说起代理,最常想到的两个词语就是静态代理和动态代理,二者却别不进行详述,对于静态代理模式由于需要大量硬编码,所以完全可以不用考虑。
针对动态代理来看,开始考虑最具代表性的CGLIB进行调研。
下面的代码为一个典型的使用CGLIB进行动态代理的样例(代理的函数为PersonService.setPerson):
public class CGlibDynamicProxy implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println(“执行前…”); Object object = methodProxy.invokeSuper(o, objects); System.out.println(“执行后…”); return object; } static class PersonService { public PersonService() { System.out.println(“PersonService构造”); } public void setPerson() { System.out.println(“PersonService:setPerson”); } } public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback(new CGlibDynamicProxy()); PersonService proxy= (PersonService) enhancer.create(); proxy.setPerson(); }}
从上面代码可以看出,对于CGLIB的动态代理而言,需要在原有代码中进行硬编码,且需要在对象初始化的时候,使用特定的方式进行初始化。因此若使用CGLIB完成MOCK,需要对应代码的的感知程度最高,达到了硬编码的程度。
2.2.2 AspectJ
由于使用代理方式无法在不对代码进行修改的情况下完成MOCK,因此我们抛弃代理方式