提到字節(jié)碼增強(qiáng)技術(shù),相信用過 Spring 的小伙伴都會(huì)知道 Java Proxy 和 Cglib。
畢竟面試準(zhǔn)備的八股文中說(shuō)過,Spring 的動(dòng)態(tài)代理有兩種實(shí)現(xiàn)方式,在有接口存在的時(shí)候使用 Java Proxy,當(dāng)沒有接口的時(shí)候使用的是 Cglib。
這兩種方式的區(qū)別不在本文的討論范圍之內(nèi),今天想給大家介紹了是另一個(gè)字節(jié)碼增強(qiáng)技術(shù) Byte Buddy。
根據(jù) Byte Buddy 官網(wǎng)所說(shuō),Byte Buddy 是一個(gè)代碼生成和操作庫(kù),用于在 Java 應(yīng)用程序運(yùn)行時(shí)創(chuàng)建和修改 Java 類,而無(wú)需編譯器的幫助。
Byte Buddy 提供一套簡(jiǎn)單易用的 API,可以很方便的使用 Java 流式編程的形式來(lái)動(dòng)態(tài)創(chuàng)建類或者創(chuàng)建接口的實(shí)現(xiàn)類,這一點(diǎn)跟 Java Proxy 和 Cglib 不一樣。
使用 Byte Buddy 的方式也非常簡(jiǎn)單,只要直接引入 Maven 依賴即可,沒有其他繁瑣的依賴。總的來(lái)說(shuō),使用 Byte Buddy 有下面的優(yōu)勢(shì):
圖片
這一份測(cè)試報(bào)告是官網(wǎng)提供的,表中的每一行分別為,類的創(chuàng)建、接口實(shí)現(xiàn)、方法調(diào)用、類型擴(kuò)展、父類方法調(diào)用的性能結(jié)果。
從性能報(bào)告中可以看出,Byte Buddy 在一些場(chǎng)景是有優(yōu)勢(shì)的,但是在有些場(chǎng)景也不見得特別有優(yōu)勢(shì),不過整體來(lái)看還是不錯(cuò)的。
說(shuō)了那么多,下面給大家演示一下,如果使用 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); } }
整個(gè)代碼的思路是通過 Byte Buddy,構(gòu)造出一個(gè) Class 對(duì)象,然后調(diào)用 Class 對(duì)象的 newInstance() 方法,再執(zhí)行 toString() 方法。上面兩個(gè)方式的功能是一樣的,寫出來(lái)更方便大家理解。
其中各個(gè)方法的含義如下:
subClass:表示構(gòu)造的類是 Object 的子類;
method:表示要構(gòu)造的具體方法,類似于過濾的功能;
intercept:表示對(duì)過濾后的方法進(jìn)行攔截;
FixedValue.value("Hello World!"):表示構(gòu)造返回一個(gè)”Hello World!“ 字符串;
make:創(chuàng)建 DynamicType.Unloaded 對(duì)象,此時(shí)這個(gè)對(duì)象被構(gòu)造出來(lái),但是還沒有被 JVM 加載,還不能使用;
load,getLoaded:加載當(dāng)前類的構(gòu)造器,并進(jìn)行加載;
等到加載到 JVM 過后,就可以使用 newInstance().toString() 進(jìn)行調(diào)用了。
上面的例子是創(chuàng)建一個(gè)簡(jiǎn)單的類和方法,下面我們介紹一個(gè)代理方法的使用,這里我們有一個(gè)目標(biāo)類 Target 和一個(gè)方法 saySomething() 方法,有一個(gè)代理類 Agent,里面有一個(gè)代理方法 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 字節(jié)碼寫入文件中 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(); }
運(yùn)行過后我們可以看到生成了一個(gè) class 文件,通過查看代碼如下,可以看到是創(chuàng)建了一個(gè) Target 的子類,并且調(diào)用了 Agent 的 agentSaySomething 方法。
圖片
Byte Buddy的 API 很豐富,這里只是很簡(jiǎn)單的給大家使用了幾個(gè) API,還有包括方法,字段的設(shè)定等等,感興趣的小伙伴可以繼續(xù)去學(xué)習(xí)學(xué)習(xí)。
本文鏈接:http://www.www897cc.com/showinfo-26-70468-0.html字節(jié)碼增強(qiáng)技術(shù),不止有 Java Proxy、 Cglib 和 Javassist 還有 Byte Buddy
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: Spring Boot項(xiàng)目集成RabbitMQ實(shí)戰(zhàn)以及坑點(diǎn)講解