日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

Byte Buddy 是一個(gè)代碼生成和操作庫(kù),用于在 JAVA 應(yīng)用程序運(yùn)行時(shí)創(chuàng)建和修改 Java 類(lèi),而無(wú)需編譯器的幫助。除了 Java 類(lèi)庫(kù)附帶的代碼生成實(shí)用程序外,Byte Buddy 還允許創(chuàng)建任意類(lèi),并且不限于實(shí)現(xiàn)用于創(chuàng)建運(yùn)行時(shí)代理的接口。

一、前言

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

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

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

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

除了這些簡(jiǎn)單的介紹外,還可以通過(guò)官網(wǎng):https://bytebuddy.NET,去了解更多關(guān)于 Byte Buddy 的內(nèi)容。

好!那么接下來(lái),我們開(kāi)始從 HelloWorld 開(kāi)始。深入了解一個(gè)技能前,先多多運(yùn)行,這樣總歸能讓找到學(xué)習(xí)的快樂(lè)。

二、開(kāi)發(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,可以關(guān)注公眾號(hào):bugstack蟲(chóng)洞棧,回復(fù)源碼下載獲取。你會(huì)獲得一個(gè)下載鏈接列表,打開(kāi)后里面的第17個(gè)「因?yàn)槲矣泻枚嚅_(kāi)源代碼」,記得給個(gè)Star!

三、案例目標(biāo)

每一個(gè)程序員,都運(yùn)行過(guò) N 多個(gè) HelloWorld,就像很熟悉的 Java;

public class Hi {

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

}

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

新知識(shí)點(diǎn)的學(xué)習(xí)不要慌,最主要是找到一個(gè)可以入手的點(diǎn),通過(guò)這樣的一個(gè)點(diǎn)去慢慢解開(kāi)整個(gè)程序的面紗。

四、技術(shù)實(shí)現(xiàn)

1. 官網(wǎng)經(jīng)典例子

在我們看官網(wǎng)文檔中,從它的介紹了就已經(jīng)提供了一個(gè)非常簡(jiǎn)單的例子,用于輸出 HelloWorld,我們?cè)谶@展示并講解下。

案例代碼:

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!

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

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

編譯后的Class文件,ByteBuddyHelloWorld.class

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

    public HelloWorld() {
    }
}

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

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

接下來(lái)的例子會(huì)通過(guò)一點(diǎn)點(diǎn)的增加代碼梳理,不斷的把一個(gè)方法完整的創(chuàng)建出來(lái)。

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

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

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("類(lèi)輸出路徑:" + pathName);
        out.write(bytes);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (null != out) try {
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 這個(gè)方法我們?cè)谥耙灿玫竭^(guò),主要就是一個(gè) Java 基礎(chǔ)的內(nèi)容,輸出字節(jié)碼到文件中。

2.2 創(chuàng)建類(lèi)信息

DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
        .subclass(Object.class)
        .name("org.itstack.demo.bytebuddy.HelloWorld")
        .make();

// 輸出類(lèi)字節(jié)碼
outputClazz(dynamicType.getBytes());
  • 創(chuàng)建類(lèi)和定義類(lèi)名,如果不寫(xiě)類(lèi)名會(huì)自動(dòng)生成要給類(lèi)名。

此時(shí)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),定義方法;名稱(chēng)、返回類(lèi)型、屬性public static
  • withParameter(String[].class, "args"),定義參數(shù);參數(shù)類(lèi)型、參數(shù)名稱(chēng)
  • intercept(FixedValue.value("Hello World!")),攔截設(shè)置返回值,但此時(shí)還能滿(mǎn)足我們的要求。

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

此時(shí)class文件:

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

    public HelloWorld() {
    }
}

此時(shí)基本已經(jīng)可以看到我們平常編寫(xiě)的 Hello World 影子了,但還能輸出結(jié)果。

2.4 委托函數(shù)使用

為了能讓我們使用字節(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();
  • 整體來(lái)看變化并不大,只有 intercept(MethodDelegation.to(Hi.class)),使用了一段委托函數(shù),真正去執(zhí)行輸出的是另外的函數(shù)方法。 MethodDelegation,需要是 public 類(lèi)被委托的方法與需要與原方法有著一樣的入?yún)?、出參、方法名,否則不能映射上

此時(shí)class文件:

public class HelloWorld {
    public static void main(String[] args) {
        Hi.main(var0);
    }

    public HelloWorld() {
    }
}
  • 那么此時(shí)就可以輸出我們需要的內(nèi)容了,Hi.main 是定義出來(lái)的委托函數(shù)。也就是一個(gè) HelloWorld

五、測(cè)試結(jié)果

為了可以讓整個(gè)方法運(yùn)行起來(lái),我們需要添加字節(jié)碼加載和反射調(diào)用的代碼塊,如下;

// 加載類(lèi)
Class<?> clazz = dynamicType.load(GenerateClazzMethod.class.getClassLoader())
        .getLoaded();

// 反射調(diào)用
clazz.getMethod("main", String[].class).invoke(clazz.newInstance(), (Object) new String[1]);

運(yùn)行結(jié)果

類(lèi)輸出路徑:/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 效果圖?

分享到:
標(biāo)簽:框架
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定