乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      字節(jié)碼編程,Byte-buddy篇一《基于Byte Buddy語法創(chuàng)建的第一個HelloWorld》

       小傅哥 2021-12-13


      作者:小傅哥
      博客:https://

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

      一、前言

      相對于小傅哥之前編寫的字節(jié)碼編程; ASM、Javassist 系列,Byte Buddy 玩法上更加高級,你可以完全不需要了解一個類和方法塊是如何通過 指令碼 LDC、LOAD、STORE、IRETURN… 生成出來的。就像它的官網介紹;

      Byte Buddy 是一個代碼生成和操作庫,用于在 Java 應用程序運行時創(chuàng)建和修改 Java 類,而無需編譯器的幫助。除了 Java 類庫附帶的代碼生成實用程序外,Byte Buddy 還允許創(chuàng)建任意類,并且不限于實現用于創(chuàng)建運行時代理的接口。此外,Byte Buddy 提供了一種方便的 API,可以使用 Java 代理或在構建過程中手動更改類。

      • 無需理解字節(jié)碼指令,即可使用簡單的 API 就能很容易操作字節(jié)碼,控制類和方法。
      • 已支持Java 11,庫輕量,僅取決于Java字節(jié)代碼解析器庫ASM的訪問者API,它本身不需要任何其他依賴項。
      • 比起JDK動態(tài)代理、cglib、Javassist,Byte Buddy在性能上具有一定的優(yōu)勢。

      2015年10月,Byte Buddy被 Oracle 授予了 Duke’s Choice大獎。該獎項對Byte Buddy的“ Java技術方面的巨大創(chuàng)新 ”表示贊賞。我們?yōu)楂@得此獎項感到非常榮幸,并感謝所有幫助Byte Buddy取得成功的用戶以及其他所有人。我們真的很感激!

      除了這些簡單的介紹外,還可以通過官網:https://,去了解更多關于 Byte Buddy 的內容。

      好! 那么接下來,我們開始從 HelloWorld 開始。深入了解一個技能前,先多多運行,這樣總歸能讓找到學習的快樂。

      二、開發(fā)環(huán)境

      1. JDK 1.8.0
      2. byte-buddy 1.10.9
      3. byte-buddy-agent 1.10.9
      4. 本章涉及源碼在:itstack-demo-bytecode-2-01,可以關注公眾號bugstack蟲洞棧,回復源碼下載獲取。你會獲得一個下載鏈接列表,打開后里面的第17個「因為我有好多開源代碼」,記得給個Star

      三、案例目標

      每一個程序員,都運行過 N 多個HelloWorld,就像很熟悉的 Java

      public class Hi {
      
          public static void main(String[] args) {
              System.out.println("Byte-buddy Hi HelloWorld By 小傅哥()");
          }
      
      }
      

      那么我們接下來就通過使用動態(tài)字節(jié)碼生成的方式,來創(chuàng)建出可以輸出 HelloWorld 的程序。

      新知識點的學習不要慌,最主要是找到一個可以入手的點,通過這樣的一個點去慢慢解開整個程序的面紗。

      四、技術實現

      1. 官網經典例子

      在我們看官網文檔中,從它的介紹了就已經提供了一個非常簡單的例子,用于輸出 HelloWorld,我們在這展示并講解下。

      案例代碼:

      String helloWorld = new ByteBuddy()
                  .subclass(Object.class)
                  .method(named("toString"))
                  .intercept(FixedValue.value("Hello World!"))
                  .make()
                  .load(getClass().getClassLoader())
                  .getLoaded()
                  .newInstance()
                  .toString();    
      
      System.out.println(helloWorld);  // Hello World!
      

      他的運行結果就是一行,Hello World!,整個代碼塊核心功能就是通過 method(named("toString")),找到 toString 方法,再通過攔截 intercept,設定此方法的返回值。FixedValue.value("Hello World!")。到這里其實一個基本的方法就通過 Byte-buddy ,改造完成。

      接下來的這一段主要是用于加載生成后的 Class 和執(zhí)行,以及調用方法 toString()。也就是最終我們輸出了想要的結果。那么,如果你不能看到這樣一段方法塊,把我們的代碼改造后的樣子,心里還是有點虛。那么,我們通過字節(jié)碼輸出到文件,看下具體被改造后的樣子,如下;

      編譯后的Class文件ByteBuddyHelloWorld.class

      public class HelloWorld {
          public String toString() {
              return "Hello World!";
          }
      
          public HelloWorld() {
          }
      }
      

      在官網來看,這是一個非常簡單并且能體現 Byte buddy 的例子。但是與我們平時想創(chuàng)建出來的 main 方法相比,還是有些差異。那么接下來,我們嘗試使用字節(jié)碼編程技術創(chuàng)建出這樣一個方法。

      2. 字節(jié)碼創(chuàng)建類和方法

      接下來的例子會通過一點點的增加代碼梳理,不斷的把一個方法完整的創(chuàng)建出來。

      2.1 定義輸出字節(jié)碼方法

      為了可以更加清晰的看到每一步對字節(jié)碼編程后,所創(chuàng)建出來的方法樣子(clazz),我們需要輸出字節(jié)碼生成 clazz。在Byte buddy中默認提供了一個 dynamicType.saveIn() 方法,我們暫時先不使用,而是通過字節(jié)碼進行保存。

      private static void outputClazz(byte[] bytes) {
          FileOutputStream out = null;
          try {
              String pathName = ApiTest.class.getResource("/").getPath() + "ByteBuddyHelloWorld.class";
              out = new FileOutputStream(new File(pathName));
              System.out.println("類輸出路徑:" + pathName);
              out.write(bytes);
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (null != out) try {
                  out.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      • 這個方法我們在之前也用到過,主要就是一個 Java 基礎的內容,輸出字節(jié)碼到文件中。

      2.2 創(chuàng)建類信息

      DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
              .subclass(Object.class)
              .name("org.itstack.demo.bytebuddy.HelloWorld")
              .make();
      
      // 輸出類字節(jié)碼
      outputClazz(dynamicType.getBytes());
      
      • 創(chuàng)建類和定義類名,如果不寫類名會自動生成要給類名。

      此時class文件:

      public class HelloWorld {
          public HelloWorld() {
          }
      }
      

      2.3 創(chuàng)建main方法

      DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
              .subclass(Object.class)
              .name("org.itstack.demo.bytebuddy.HelloWorld")
              .defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC)
              .withParameter(String[].class, "args")
              .intercept(FixedValue.value("Hello World!"))
              .make();
      

      與上面相比新增的代碼片段;

      • defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC),定義方法;名稱、返回類型、屬性public static
      • withParameter(String[].class, "args"),定義參數;參數類型、參數名稱
      • intercept(FixedValue.value("Hello World!")),攔截設置返回值,但此時還能滿足我們的要求。

      這里有一個知識點,Modifier.PUBLIC + Modifier.STATIC,這是一個是二進制相加,每一個類型都在二進制中占有一位。例如 1 2 4 8 ... 對應的二進制占位 1111。所以可以執(zhí)行相加運算,并又能保留原有單元的屬性。

      此時class文件:

      public class HelloWorld {
          public static void main(String[] args) {
              String var10000 = "Hello World!";
          }
      
          public HelloWorld() {
          }
      }
      

      此時基本已經可以看到我們平常編寫的 Hello World 影子了,但還能輸出結果。

      2.4 委托函數使用

      為了能讓我們使用字節(jié)碼編程創(chuàng)建的方法去輸出一段 Hello World ,那么這里需要使用到委托

      DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
              .subclass(Object.class)
              .name("org.itstack.demo.bytebuddy.HelloWorld")
              .defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC)
              .withParameter(String[].class, "args")
              .intercept(MethodDelegation.to(Hi.class))
              .make();
      
      • 整體來看變化并不大,只有 intercept(MethodDelegation.to(Hi.class)),使用了一段委托函數,真正去執(zhí)行輸出的是另外的函數方法。

        • MethodDelegation,需要是 public
        • 被委托的方法與需要與原方法有著一樣的入參、出參、方法名,否則不能映射上

      此時class文件:

      public class HelloWorld {
          public static void main(String[] args) {
              Hi.main(var0);
          }
      
          public HelloWorld() {
          }
      }
      
      • 那么此時就可以輸出我們需要的內容了,Hi.main 是定義出來的委托函數。也就是一個 HelloWorld

      五、測試結果

      為了可以讓整個方法運行起來,我們需要添加字節(jié)碼加載和反射調用的代碼塊,如下;

      // 加載類
      Class<?> clazz = dynamicType.load(GenerateClazzMethod.class.getClassLoader())
              .getLoaded();
      
      // 反射調用
      clazz.getMethod("main", String[].class).invoke(clazz.newInstance(), (Object) new String[1]);
      

      運行結果

      類輸出路徑:/User/xiaofuge/itstack/git/github.com/itstack-demo-bytecode/itstack-demo-bytecode-2-01/target/test-classes/ByteBuddyHelloWorld.class
      helloWorld
      
      Process finished with exit code 0
      

      效果圖

      Byte buddy HelloWorld 效果圖

      六、總結

      • 在本章節(jié) Byte buddy 中,需要掌握幾個關鍵信息;創(chuàng)建方法、定義屬性、攔截委托、輸出字節(jié)碼,以及最終的運行。這樣的一個簡單過程,可以很快的了解到如何使用 Byte buddy
      • 本系列文章后續(xù)會繼續(xù)更新,把常用的 Byte buddy 方法通過實際的案例去模擬建設,在這個過程中加強學習使用。一些基礎知識也可以通過官方文檔進行學習;https://。
      • 在學習整理的過程中發(fā)現,關于字節(jié)碼編程方面的資料并不是很全,主要源于大家平時的開發(fā)中基本是用不到的,誰也不可能總去修改字節(jié)碼。但對于補全這樣的成體系完善技術棧資料,卻可以幫助很多需要的人。因此我也會持續(xù)輸出類似這樣空白的技術文章。

      七、彩蛋

      CodeGuide | 程序員編碼指南 Go!
      本代碼庫是作者小傅哥多年從事一線互聯網 Java 開發(fā)的學習歷程技術匯總,旨在為大家提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心內容。如果本倉庫能為您提供幫助,請給予支持(關注、點贊、分享)!

      CodeGuide | 程序員編碼指南

        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多