日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Jmock 原理简单说明

發布時間:2023/12/19 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Jmock 原理简单说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在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 原理简单说明的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。