提到字節碼增強技術,相信用過 Spring 的小伙伴都會知道 Java Proxy 和 Cglib。
畢竟面試準備的八股文中說過,Spring 的動態代理有兩種實現方式,在有接口存在的時候使用 Java Proxy,當沒有接口的時候使用的是 Cglib。
這兩種方式的區別不在本文的討論范圍之內,今天想給大家介紹了是另一個字節碼增強技術 Byte Buddy。
根據 Byte Buddy 官網所說,Byte Buddy 是一個代碼生成和操作庫,用于在 Java 應用程序運行時創建和修改 Java 類,而無需編譯器的幫助。
Byte Buddy 提供一套簡單易用的 API,可以很方便的使用 Java 流式編程的形式來動態創建類或者創建接口的實現類,這一點跟 Java Proxy 和 Cglib 不一樣。
使用 Byte Buddy 的方式也非常簡單,只要直接引入 Maven 依賴即可,沒有其他繁瑣的依賴。總的來說,使用 Byte Buddy 有下面的優勢:
圖片
這一份測試報告是官網提供的,表中的每一行分別為,類的創建、接口實現、方法調用、類型擴展、父類方法調用的性能結果。
從性能報告中可以看出,Byte Buddy 在一些場景是有優勢的,但是在有些場景也不見得特別有優勢,不過整體來看還是不錯的。
說了那么多,下面給大家演示一下,如果使用 Byte Buddy,首先我們需要引入 Maven 依賴,我這里用的版本是 1.14.6,也可以使用其他版本。
<dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.6</version></dependency>
public static void test1() { try { Class<?> dynamicType = new ByteBuddy(). subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value("Hello World!")) .make() .load(ByteBuddyDemo.class.getClassLoader()) .getLoaded(); System.out.println(dynamicType.newInstance().toString()); } catch (Exception e) { System.out.println(e.getMessage()); } }public static void test2() { try { DynamicType.Unloaded<Object> unloaded = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value("Hello World!")) .make(); DynamicType.Loaded<Object> load = unloaded.load(ByteBuddyDemo.class.getClassLoader()); System.out.println(load.getLoaded().newInstance().toString()); } catch (Exception e) { throw new RuntimeException(e); } }
整個代碼的思路是通過 Byte Buddy,構造出一個 Class 對象,然后調用 Class 對象的 newInstance() 方法,再執行 toString() 方法。上面兩個方式的功能是一樣的,寫出來更方便大家理解。
其中各個方法的含義如下:
subClass:表示構造的類是 Object 的子類;
method:表示要構造的具體方法,類似于過濾的功能;
intercept:表示對過濾后的方法進行攔截;
FixedValue.value("Hello World!"):表示構造返回一個”Hello World!“ 字符串;
make:創建 DynamicType.Unloaded 對象,此時這個對象被構造出來,但是還沒有被 JVM 加載,還不能使用;
load,getLoaded:加載當前類的構造器,并進行加載;
等到加載到 JVM 過后,就可以使用 newInstance().toString() 進行調用了。
上面的例子是創建一個簡單的類和方法,下面我們介紹一個代理方法的使用,這里我們有一個目標類 Target 和一個方法 saySomething() 方法,有一個代理類 Agent,里面有一個代理方法 agentSaySomething(),如下所示:
public class Target { public String saySomething() { return "Hello target"; }}public class Agent { public static String agentSaySomething() { System.out.println("agentSaySomething"); return "hello agent"; }}public static void test4() { try { DynamicType.Unloaded<Target> agent = new ByteBuddy() .subclass(Target.class) .method(named("saySomething") .and(isDeclaredBy(Target.class) .and(returns(String.class)))) .intercept(MethodDelegation.to(Agent.class)) .make(); // 將 agent 字節碼寫入文件中 outputClazz(agent.getBytes()); } catch (Exception e) { throw new RuntimeException(e); } } private static void outputClazz(byte[] bytes) { FileOutputStream out = null; try { String pathName = ByteBuddyDemo.class.getResource("/").getPath() + "AgentTarget.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(); } } } public static void main(String[] args) { test4(); }
運行過后我們可以看到生成了一個 class 文件,通過查看代碼如下,可以看到是創建了一個 Target 的子類,并且調用了 Agent 的 agentSaySomething 方法。
圖片
Byte Buddy的 API 很豐富,這里只是很簡單的給大家使用了幾個 API,還有包括方法,字段的設定等等,感興趣的小伙伴可以繼續去學習學習。
本文鏈接:http://www.www897cc.com/showinfo-26-70468-0.html字節碼增強技術,不止有 Java Proxy、 Cglib 和 Javassist 還有 Byte Buddy
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com