作者:小傅哥
部落格:https://bugstack.cn
沉澱、分享、成長,讓自己和他人都能有所收穫!
一、前言
通過前面兩篇 javassist
的基本內容,大體介紹了;類池(ClassPool)、類(CtClass)、屬性(CtField)、方法(CtMethod),的使用方式,並通過建立不同型別的入參出參方法,基本可以掌握如何使用這樣的程式碼結構進行位元組碼程式設計。
那麼,今天我們嘗試使用 javassist
去修改一個正在執行中的類裡面的方法內容。也就是在執行時重新載入類資訊
可能在你平時的 CRUD 開發中並沒有想到過這樣的 燒操作,但它卻有很多的應用場景在使用,例如;
- 熱部署常用在生產環境中,主要由於這樣的系統不能頻繁啟停且啟動耗時較長的應用。
- 另外一些元件化風控模型包,給外部使用。當模型包進行升級時並不需要外部重新部署,甚至不需要讓你知道升級了。
- 再者會用於開發、除錯中,可以非常有效的提升編碼效率,解放碼農的右手和左手。
人的大腦很難創造未知的事物,所以需要學習。請多看小傅哥的碼文,少搞CRUD
關於位元組程式設計中所有涉及的程式碼,都可以通過關注公眾號
:bugstack蟲洞棧,回覆:原始碼,進行獲取。
二、開發環境
- JDK 1.8.0
- jdk1.8.0_161libtools.jar - 需要使用到
jdi
包 - javassist 3.12.1.GA
三、案例目標
為了讓案例目標更具色彩
,我們模擬一個謝飛機老婆,通過系統查詢自己男朋友前女友數量
的 危機 方法,需要緊急處理的過程。
為了保障家庭的和諧化解危機,我們通過動態重新載入類,將謝飛機前女友數量修改為0
並返回。依次安定家庭和諧。最終謝飛機會給我錢,當做報酬
讓謝飛機很慌的方法
public class ApiTest {
public String queryGirlfriendCount(String boyfriendName) {
return boyfriendName + "的前女友數量:" + (new Random().nextInt(10) + 1) + " 個";
}
}
可預見的結果;
你到底幾個前女友!!!
謝飛機的前女友數量:3 個
謝飛機的前女友數量:5 個
謝飛機的前女友數量:8 個
四、技術實現
1. HotSwapper 操作類熱載入
德萊聯盟,王牌工程師,申請出棧
/**
* 公眾號:bugstack蟲洞棧
* 部落格棧:https://bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!
* 本專欄是小傅哥多年從事一線網際網路Java開發的學習歷程技術彙總,旨在為大家提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心內容。如果能為您提供幫助,請給予支援(關注、點贊、分享)!
*/
public class GenerateClazzMethod {
public static void main(String[] args) throws Exception {
ApiTest apiTest = new ApiTest();
System.out.println("你到底幾個前女友!!!");
// 模擬謝飛機老婆一頓查詢
new Thread(() -> {
while (true){
System.out.println(apiTest.queryGirlfriendCount("謝飛機"));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 監聽 8000 埠,在啟動引數裡設定
// java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
HotSwapper hs = new HotSwapper(8000);
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(ApiTest.class.getName());
// 獲取方法
CtMethod ctMethod = ctClass.getDeclaredMethod("queryGirlfriendCount");
// 重寫方法
ctMethod.setBody("{ return $1 + \"的前女友數量:\" + (0L) + \" 個\"; }");
// 載入新的類
System.out.println(":: 執行HotSwapper熱插拔,修改謝飛機前女友數量為0個!");
hs.reload(ApiTest.class.getName(), ctClass.toBytecode());
}
}
2. 知識點講解
- 多執行緒模擬迴圈呼叫,這個方法會一直執行查詢。在後續修改類之後輸出的結果資訊會有不同。
-
javassist.tools.HotSwapper
,是javassist
的包中提供的熱載入替換類操作。在執行時需要啟用 JPDA(Java平臺偵錯程式體系結構)。 -
ctMethod.setBody
,重寫方法的內容在上面兩個章節已經很清楚的描述了。$1 是獲取方法中的第一個入參,大括號{}
裡是具體執行替換的方法體。 - 最後使用
hs.reload
執行熱載入替換操作,這裡的ctClass.toBytecode()
獲取的是處理後類的位元組碼。
五、測試結果
1. 引入tools.jar
2. 配置-agentlib
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
3. 執行測試
Listening for transport dt_socket at address: 8000
你到底幾個前女友!!!
謝飛機的前女友數量:3 個
謝飛機的前女友數量:5 個
謝飛機的前女友數量:8 個
:: 執行HotSwapper熱插拔,修改謝飛機前女友數量為0個!
謝飛機的前女友數量:4 個
謝飛機的前女友數量:5 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
謝飛機的前女友數量:0 個
...
Process finished with exit code -1
當看到前女友數量為 0 時,謝飛機露出了羞澀的微笑,並兌現了承諾,將4毛錢給了王牌工程師小傅哥
。
4. 效果演示
六、總結
- 沒得辦法,即使再好的技術不加點段子也沒人看。只能坑我兄弟飛機了!德萊聯盟,王牌工程師,申請出
棧
- 關於熱載入修改類的操作,在實際場景中還是蠻多的,但一般都是比較苛刻的場景訴求。在平時開發中還是比較少遇到的,並且CRUD開發不會遇到。
-
Javassist
對ASM
這樣的位元組碼操作封裝起來提供的API
確實很好操作,在一些場景下也不需要考慮JVM
中區域性變數和運算元棧。但如果需要更高的效能,可以考慮使用ASM
。