使用 Javassist 在運行時從新加載類「替換原方法輸出不同的結果」| 字節碼編程 · Javassist 03


做者:小傅哥
博客:https://bugstack.cnjava

沉澱、分享、成長,讓本身和他人都能有所收穫!

1、前言

經過前面兩篇 javassist 的基本內容,大致介紹了;類池(ClassPool)、類(CtClass)、屬性(CtField)、方法(CtMethod),的使用方式,並經過建立不一樣類型的入參出參方法,基本能夠掌握如何使用這樣的代碼結構進行字節碼編程。編程

那麼,今天咱們嘗試使用 javassist 去修改一個正在執行中的類裏面的方法內容。也就是在運行時從新加載類信息api

可能在你平時的 CRUD 開發中並無想到過這樣的 燒操做,但它卻有不少的應用場景在使用,例如;多線程

  1. 熱部署經常使用在生產環境中,主要因爲這樣的系統不能頻繁啓停且啓動耗時較長的應用。
  2. 另一些組件化風控模型包,給外部使用。當模型包進行升級時並不須要外部從新部署,甚至不須要讓你知道升級了。
  3. 再者會用於開發、調試中,能夠很是有效的提高編碼效率,解放碼農的右手左手

人的大腦很難創造未知的事物,因此須要學習。請多看小傅哥的碼文,少搞CRUDapp

關於字節編程中全部涉及的代碼,均可以經過關注公衆號bugstack蟲洞棧,回覆:源碼,進行獲取。dom

2、開發環境

  1. JDK 1.8.0
  2. jdk1.8.0_161libtools.jar - 須要使用到 jdi
  3. javassist 3.12.1.GA

3、案例目標

爲了讓案例目標更具色彩,咱們模擬一個謝飛機老婆,經過系統查詢本身男友前女朋友數量危機 方法,須要緊急處理的過程。socket

爲了保障家庭的和諧化解危機,咱們經過動態從新加載類,將謝飛機前女朋友數量修改成0並返回。依次安定家庭和諧。最終謝飛機會給我錢,當作報酬組件化

德萊聯盟,王牌工程師,申請出棧

讓謝飛機很慌的方法性能

public class ApiTest {

    public String queryGirlfriendCount(String boyfriendName) {
        return boyfriendName + "的前女朋友數量:" + (new Random().nextInt(10) + 1) + " 個";
    }

}

可預見的結果學習

你到底幾個前女朋友!!!
謝飛機的前女朋友數量:3 個
謝飛機的前女朋友數量:5 個
謝飛機的前女朋友數量:8 個

4、技術實現

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. 知識點講解

  1. 多線程模擬循環調用,這個方法會一直執行查詢。在後續修改類以後輸出的結果信息會有不一樣。
  2. javassist.tools.HotSwapper,是 javassist 的包中提供的熱加載替換類操做。在執行時須要啓用 JPDA(Java平臺調試器體系結構)。
  3. ctMethod.setBody,重寫方法的內容在上面兩個章節已經很清楚的描述了。$1 是獲取方法中的第一個入參,大括號{}裏是具體執行替換的方法體。
  4. 最後使用 hs.reload 執行熱加載替換操做,這裏的 ctClass.toBytecode() 獲取的是處理後類的字節碼。

5、測試結果

1. 引入tools.jar

Open Module Settings,引入tools.jar

2. 配置-agentlib

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000

VM options,配置-agentlib

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毛錢給了王牌工程師小傅哥

來自謝飛機的code4毛錢/code收入

4. 效果演示

熱加載救火,成功拿到4毛錢

6、總結

  1. 沒得辦法,即便再好的技術不加點段子也沒人看。只能坑我兄弟飛機了!德萊聯盟,王牌工程師,申請出
  2. 關於熱加載修改類的操做,在實際場景中仍是蠻多的,但通常都是比較苛刻的場景訴求。在平時開發中仍是比較少遇到的,而且CRUD開發不會遇到。
  3. JavassistASM 這樣的字節碼操做封裝起來提供的API確實很好操做,在一些場景下也不須要考慮 JVM 中局部變量和操做數棧。但若是須要更高的性能,能夠考慮使用 ASM

相關文章
相關標籤/搜索