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


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

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

1、前言

在上一篇 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蟲洞棧,回覆碼下載獲取」學習

2、開發環境

  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>

3、案例目標

爲了練習屬性字段和方法的不一樣的入參、出參,咱們使用 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;
    }

}

4、技術實現

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();

    }

}

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

  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 進行轉換。

下面這張基本描述了一個類方法在建立時候不一樣參數的含義,能夠參考。spa

Javassist 建立類方法入參描述

5、測試結果

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

在測試以前,咱們須要寫一點反射代碼來調用類的方法code

// 測試調用
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);

測試結果:xml

圓面積:4.750506
兩數和:3.0

Process finished with exit code 0

2. 查看使用Javassist生成的類

Javassist 生成的類內容

6、總結

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

相關文章
相關標籤/搜索