字節碼編程,Javassist篇二《定義屬性以及創建方法時多種入參和出參類型的使用》


作者:小傅哥
博客:https://bugstack.cn

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

一、前言

在上一篇 Helloworld 中,我們初步嘗試使用了 Javassist字節編程的方式,來創建我們的方法體並通過反射調用運行了結果。大致瞭解到創建在使用字節碼編程的時候基本離不開三個核心類;ClassPoolCtClassCtMethod,它們分別管理着對象容器、類和方法。但是我們還少用一樣就是字段;CtFields,在這一章節中我們不止會使用字段,還會創建多個不同入參類型和返回值的學習。

在學習之前先重點列一下相關的知識點,如下;

  1. CtClass.doubleTypeintTypefloatType8 個基本類型和一個voidType,也就是空的返回類型。
  2. 傳遞和返回的是對象類型時,那麼需要時用;pool.get(Double.class.getName(),進行設置。
  3. 當需要設置多個入參時,需要在數組中以此設置入參類型;new CtClass[]{CtClass.doubleType, CtClass.doubleType}
  4. 在方法體中需要取得入參並計算時,需要使用 $1$2 …,數字表示入參的位置。$0this
  5. 設置屬性字段,並賦值
  6. Javassist 中的裝箱/拆箱

!那麼我們就開始對這些知識點進行應用,創建出類和對應的方法。「所有代碼都可以關注公衆號:bugstack蟲洞棧,回覆碼下載獲取」

二、開發環境

  1. JDK 1.8.0
  2. javassist 3.12.1.GA
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
    <type>jar</type>
</dependency>

三、案例目標

爲了練習屬性字段和方法的不同的入參、出參,我們使用 javassist 創建如下這樣的方法。當然你也可以嘗試去擴展其他類型的方法。

public class ApiTest {

    private double π = 3.14D;

    //S = πr²
    public double calculateCircularArea(int r) {
        return π * r * r;
    }

    //S = a + b
    public double sumOfTwoNumbers(double a, double b) {
        return a + b;
    }

}

四、技術實現

GenerateClazzMethod.java & 生成類和方法

/**
 * 公衆號:bugstack蟲洞棧
 * 博客棧:https://bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!
 * 本專欄是小傅哥多年從事一線互聯網Java開發的學習歷程技術彙總,旨在爲大家提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心內容。如果能爲您提供幫助,請給予支持(關注、點贊、分享)!
 */
public class GenerateClazzMethod {

    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.makeClass("org.itstack.demo.javassist.MathUtil");

        // 屬性字段
        CtField ctField = new CtField(CtClass.doubleType, "π", ctClass);
        ctField.setModifiers(Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL);
        ctClass.addField(ctField, "3.14");

        // 方法:求圓面積
        CtMethod calculateCircularArea = new CtMethod(CtClass.doubleType, "calculateCircularArea", new CtClass[]{CtClass.doubleType}, ctClass);
        calculateCircularArea.setModifiers(Modifier.PUBLIC);
        calculateCircularArea.setBody("{return π * $1 * $1;}");
        ctClass.addMethod(calculateCircularArea);

        // 方法;兩數之和
        CtMethod sumOfTwoNumbers = new CtMethod(pool.get(Double.class.getName()), "sumOfTwoNumbers", new CtClass[]{CtClass.doubleType, CtClass.doubleType}, ctClass);
        sumOfTwoNumbers.setModifiers(Modifier.PUBLIC);
        sumOfTwoNumbers.setBody("{return Double.valueOf($1 + $2);}");
        ctClass.addMethod(sumOfTwoNumbers);
        // 輸出類的內容
        ctClass.writeFile();

    }

}

這裏面有幾個核心點,講解如下;

  1. CtField,屬性字段的創建。這就像我們正常寫代碼一樣,需要設定屬性的;名稱、類型以及是 public 的還是 private 的以及 staticfinal 等。都可以通過 Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL,通過組合來控制。同樣這也適用於對方法類型的設置。同時需要在添加屬性的地方,設置初始值。
  2. 接下來是我們設置了一個求圓面積的方法,如果說在方法體中需要使用到入參類型。那麼需要通過符號 $+數字,來獲取入參。這個數字就是當前入參的位置。比如取第一個入參:$1,以此類推。
  3. 之後是我們的多種入參類型,在這開始我們也提到了。如果是基本類型入參都可以使用 CtClass.doubleType,對象類型入參使用 pool.get(類.class.getName) 獲取。
  4. 最終同樣我們會把使用字節碼編譯的 class 輸出到工程目錄下 ctClass.writeFile()
  5. 在Javassist中並不會給類型做拆箱和裝箱操作,需要顯式的處理。例如上面案例中,需要將 double 使用 Double.valueOf 進行轉換。

下面這張基本描述了一個類方法在創建時候不同參數的含義,可以參考。

Javassist 創建類方法入參描述

五、測試結果

1. 反射調用字節碼類方法

在測試之前,我們需要寫一點反射代碼來調用類的方法

// 測試調用
Class clazz = ctClass.toClass();
Object obj = clazz.newInstance();

Method method_calculateCircularArea = clazz.getDeclaredMethod("calculateCircularArea", double.class);
Object obj_01 = method_calculateCircularArea.invoke(obj, 1.23);
System.out.println("圓面積:" + obj_01);

Method method_sumOfTwoNumbers = clazz.getDeclaredMethod("sumOfTwoNumbers", double.class, double.class);
Object obj_02 = method_sumOfTwoNumbers.invoke(obj, 1, 2);
System.out.println("兩數和:" + obj_02);

測試結果:

圓面積:4.750506
兩數和:3.0

Process finished with exit code 0

2. 查看使用Javassist生成的類

Javassist 生成的類內容

六、總結

  1. 本篇案例中重點強調了屬性字段創建,同時需要給屬性字段賦值。
  2. Javassist 是不會進行類型的自動裝箱和拆箱的,需要我們進行手動處理,否則生成類在執行會報類型錯誤。
  3. 當需要使用入參的時候,可以使用 $1 來獲取。這也是後續做一些監控獲取入參的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章