如何把Java代碼玩出花?JVM Sandbox入門教程與原理淺談( 三 )

最常用的是loadCompleted,所以我們重寫loadCompleted類,在里面開啟我們的監控類SpringBeanStartMonitor線程 。
而SpringBeanStartMonitor的核心代碼如下圖:

如何把Java代碼玩出花?JVM Sandbox入門教程與原理淺談

文章插圖
使用Sandbox的doClassFilter過濾出匹配的類,這里我們是BeanFactory 。
使用doMethodFilter過濾出要監聽的方法,這里是initializeBean 。
里取initializeBean作為統計耗時的切入方法 。具體為什么選擇該方法,涉及到SpringBean的啟動生命周期,不在本文贅述范圍內 。(本文作者:蠻三刀醬)
如何把Java代碼玩出花?JVM Sandbox入門教程與原理淺談

文章插圖
接著使用moduleEventWatcher.watch(springBeanFilter, springBeanInitListener, Event.Type.BEFORE, Event.Type.RETURN);
將我們的springBeanInitListener監聽器綁定到被觀測的方法上 。這樣每次initializeBean被調用,都會走到我們的監聽器邏輯 。
監聽器的主要邏輯如下:
如何把Java代碼玩出花?JVM Sandbox入門教程與原理淺談

文章插圖
代碼有點長,不必細看,主要就是在原方法的BeforeEvent(進入前)和ReturnEvent(執行正常返回后)執行上述的切面邏輯,我這里便是使用了一個MAP存儲每個Bean的初始化開始和結束時間,最終統計出初始化耗時 。
最終 , 我們還需要一個方法來知道我們的原始Spring應用已經啟動完畢,這樣我們可以手動卸載我們的Sandbox模塊,畢竟他已經完成了他的歷史使命,不需要再依附在主進程上 。
我們通過一個簡陋的辦法,檢查http://127.0.0.1:8080/是否會返回小于500的狀態碼 , 來判斷Spring容器是否已經啟動 。當然如果你的Spring沒有使用Web框架,就不能用這個方法來判斷啟動完成,你也許可以通過Spring自己的生命周期鉤子函數來實現,這里我是偷了個懶 。
整個SpringBean監聽模塊的開發就完成了,你可以感受到 , 你的開發和日常業務開發幾乎沒有區別 , 這就是JVM Sandbox帶給你的最大好處 。
上述源碼放在了我的Github倉庫:
https://github.com/monitor4all/javaMonitor
JVM Sandbox底層技術整個JVM Sandbox的入門使用基本上講完了,上文提到了一些JVM技術名詞,可能小伙伴們聽過但不是特別了解 。這里簡單闡述幾個重要的概念 , 理清楚這幾個概念之間的關系,以便大家更好的理解JVM Sandbox底層的實現 。
JVMTIJVMTI(JVM Tool Interface)是 Java 虛擬機所提供的 native 編程接口,JVMTI可以用來開發并監控虛擬機,可以查看JVM內部的狀態 , 并控制JVM應用程序的執行 ??蓪崿F的功能包括但不限于:調試、監控、線程分析、覆蓋率分析工具等 。
很多java監控、診斷工具都是基于這種形式來工作的 。如果arthas、jinfo、brace等,雖然這些工具底層是JVM TI,但是它們還使用到了上層工具JavaAgent 。
JavaAgent和InstrumentationJavaagent是java命令的一個參數 。參數 javaagent 可以用于指定一個 jar 包 。
-agentlib:<libname>[=<選項>] 加載本機代理庫 <libname>, 例如 -agentlib:hprof 另請參閱 -agentlib:jdwp=help 和 -agentlib:hprof=help-agentpath:<pathname>[=<選項>] 按完整路徑名加載本機代理庫-javaagent:<jarpath>[=<選項>] 加載 Java 編程語言代理, 請參閱 java.lang.instrument在上面-javaagent參數中提到了參閱java.lang.instrument,這是在rt.jar 中定義的一個包 , 該包提供了一些工具幫助開發人員在 Java 程序運行時,動態修改系統中的 Class 類型 。其中,使用該軟件包的一個關鍵組件就是 Javaagent 。從名字上看,似乎是個 Java 代理之類的,而實際上,他的功能更像是一個Class 類型的轉換器,他可以在運行時接受重新外部請求 , 對Class類型進行修改 。
Instrumentation的底層實現依賴于JVMTI 。
JVM 會優先加載 帶 Instrumentation 簽名的方法,加載成功忽略第二種,如果第一種沒有 , 則加載第二種方法 。
Instrumentation支持的接口:
public interface Instrumentation {//添加一個ClassFileTransformer//之后類加載時都會經過這個ClassFileTransformer轉換void addTransformer(ClassFileTransformer transformer, boolean canRetransform);void addTransformer(ClassFileTransformer transformer);//移除ClassFileTransformerboolean removeTransformer(ClassFileTransformer transformer);boolean isRetransformClassesSupported();//將一些已經加載過的類重新拿出來經過注冊好的ClassFileTransformer轉換//retransformation可以修改方法體,但是不能變更方法簽名、增加和刪除方法/類的成員屬性void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;boolean isRedefineClassesSupported();//重新定義某個類void redefineClasses(ClassDefinition... definitions)throwsClassNotFoundException, UnmodifiableClassException;boolean isModifiableClass(Class<?> theClass);@SuppressWarnings("rawtypes")Class[] getAllLoadedClasses();@SuppressWarnings("rawtypes")Class[] getInitiatedClasses(ClassLoader loader);long getObjectSize(Object objectToSize);void appendToBootstrapClassLoaderSearch(JarFile jarfile);void appendToSystemClassLoaderSearch(JarFile jarfile);boolean isNativeMethodPrefixSupported();void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);}

推薦閱讀