最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java字節(jié)碼ByteBuddy使用及原理解析下

 更新時間:2023年05月18日 11:54:03   作者:騎牛上青山  
這篇文章主要為大家介紹了Java字節(jié)碼ByteBuddy使用及原理解析下篇,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

構(gòu)建Java Agent

在應(yīng)用程序中很多時候都不方便直接修改代碼,java agent模式可以不用直接修改應(yīng)用的代碼就能夠?qū)崿F(xiàn)自己的功能。使用ByteBuddy可以讓我們很容易構(gòu)建自己的agent。事實上很多的開源Agent都是借助的ByteBuddy來實現(xiàn)的Agent。關(guān)于java agent我后續(xù)會寫一些文章來進一步深入介紹相關(guān)內(nèi)容,在此就不多贅述了。

處理泛型

Java的泛型會在運行時進行類型擦除。但是,由于泛型類型可能被嵌入到任何Java類文件中,并由 Java反射API對外暴露。所以將通用信息包含到生成的類中是有意義的。

由此種種,在子類化類、實現(xiàn)接口或聲明字段或方法時,ByteBuddy接受Type的參數(shù)而不是擦除的Class。也可以使用 TypeDescription.Generic.Builder 明確定義泛型類型。

字段和方法

上述的章節(jié)講述了類的創(chuàng)建與修改,接下來就要講講字段和方法的處理了。其實在上文中我們也已經(jīng)舉過了相關(guān)的例子了。我們引用了一個將類的方法替換成其他返回值的例子:

new ByteBuddy()
            .subclass(Object.class)
            .method(ElementMatchers.named("toString"))
            .intercept(FixedValue.value("Hello World!"))
            .make()
            .saveIn(new File("result"));

現(xiàn)在我們仔細的審視上述的代碼,在method方法中使用到了ElementMatchers.named方法,這個方法是ElementMatchers中定義的一系列方法的其中一種,這個類主要用于創(chuàng)建易于人類閱讀的類和方法匹配機制。其中定義了大量的方法來助于定義類和方法。

例如:

named("toString").and(returns(String.class)).and(takesArguments(0))

上述代碼就是描述的名稱為toString,返回值為String且沒有參數(shù)的方法

接下來來看一個復(fù)雜的案例:

class Foo {
  public String bar() { return null; }
  public String foo() { return null; }
  public String foo(Object o) { return null; }
}
Foo dynamicFoo = new ByteBuddy()
  .subclass(Foo.class)
  .method(isDeclaredBy(Foo.class)).intercept(FixedValue.value("One!"))
  .method(named("foo")).intercept(FixedValue.value("Two!"))
  .method(named("foo").and(takesArguments(1))).intercept(FixedValue.value("Three!"))
  .make()
  .load(getClass().getClassLoader())
  .getLoaded()
  .newInstance();

在這個例子中定義了三個方法匹配,依據(jù)ByteBuddy的實現(xiàn)原則,上述的調(diào)用是基于堆棧的形式,因此在最后的.method(named("foo").and(takesArguments(1))).intercept(FixedValue.value("Three!"))反而會被最先匹配,當他未匹配成功時會以堆棧的順序依次匹配。

如果不想覆蓋方法,想要重新定義自己的方法,可以使用defineMethod,當然這也符合上述的堆棧的執(zhí)行順序。

深入了解FixedValue

在上文中我們已經(jīng)有了一些使用FixedValue的例子了。顧名思義,FixedValue的作用是返回一個固定的提供的對象。

類會議如下兩種方式記錄這個對象:

  • 固定值寫入類的常量池。常量池主要記住類的屬性,比如類名或者方法名。除了這些反射屬性之外,常量池還有空間來存儲在類的方法或字段中使用的任何字符串或原始值。除了字符串和原始值,類池還可以存儲對其他類型的引用。
  • 該值存儲在類的靜態(tài)字段中。所以一旦將類加載到Java虛擬機中,就必須為該字段分配給定值。

當你使用FixedValue.value(Object)時,ByteBuddy會分析參數(shù)的類型,并且存儲下來(優(yōu)先嘗試第一種方法,不可行才會使用第二種方法)。但是請注意,如果值存儲在類池中,則所選方法返回的實例可能具有不同的對象標識。這種時候就可以使用FixedValue.reference(Object)來始終將對象存儲在靜態(tài)字段中。

委托方法調(diào)用

在很多場景下使用FixedValue返回固定值顯然是遠遠不夠的。所以ByteBuddy提供了MethodDelegation來支持更加強大的和自由的方法定義。

看這個例子:

class Source {
        public String hello(String name) { return null; }
    }
    class Target {
        public static String hello(String name) {
            return "Hello " + name + "!";
        }
    }
    String helloWorld = new ByteBuddy()
            .subclass(Source.class)
            .method(named("hello")).intercept(MethodDelegation.to(Target.class))
            .make()
            .load(ClassLoader.getSystemClassLoader())
            .getLoaded()
            .newInstance()
            .hello("World");
    System.out.println(helloWorld);

在這個例子里面我們把Sourcehello方法委托給了Target,因此程序輸出了Hello World!而不是null

為了實現(xiàn)上述的效果MethodDelegation會找到Target的所有可以調(diào)用的方法并且進行最佳匹配。在上述方法中因為只有一個方法,因此匹配非常簡單,那么遇到復(fù)雜的情況MethodDelegation會怎么進行匹配呢?我們看下一個例子:

class Target {
  public static String intercept(String name) { return "Hello " + name + "!"; }
  public static String intercept(int i) { return Integer.toString(i); }
  public static String intercept(Object o) { return o.toString(); }
}

這個例子中的Target有三個重載方法,我們用這個類來進行測試。經(jīng)過測試,最后輸出的結(jié)果是Hello World!,可能有人會疑惑為什么連方法名都完全不一樣,這也能被委托嗎?

這里就涉及到了ByteBuddy的實現(xiàn)了。ByteBuddy不要求目標方法和源方法同名,回看上述方法,顯然最后綁定的是第一個intercept方法,這是為什么呢?首先,第二個方法入?yún)閕nt顯然無法匹配,但是第一和第三個方法應(yīng)該如何選擇,這就又涉及到了內(nèi)部的實現(xiàn)問題。ByteBuddy模仿了java編譯器綁定重載方法的實現(xiàn)方式,總是選擇“最具體”的類型來進行綁定。而String顯然比Object更為具體,因此綁定到了第一個intercept方法。

MethodDelegation可以配合注解@Argument一起使用,@Argument可以通過配置參數(shù)的位置(排在第n個)來進行參數(shù)的綁定。實際上如果你沒有配置此注解,ByteBuddy也會按照注解綁定的方式來處理,例如:

void foo(Object o1, Object o2)

如果原始方法是這樣的,那么ByteBuddy會進行如下的解析:

void foo(@Argument(0) Object o1, @Argument(1) Object o2)

第一個參數(shù)和第二個參數(shù)會被分配到對應(yīng)的攔截器,如果被攔截的方法少于兩個參數(shù),或者參數(shù)類型不能匹配,那么就舍棄攔截方法。

MethodDelegation還可以配合很多的注解來處理不同的場景:

  • @AllArguments:此配置為數(shù)組類型,包含所有源方法的參數(shù)。為此,所有源方法參數(shù)都必須是可分配給數(shù)組的類型。如果不是此方法在匹配時會被舍棄。
  • @This:這個注解可以用于獲取當前實例

@Origin:此注解用于獲取方法的簽名,例如:

public static String intercept(@Origin String method) { return "Hello " + method + "!"; }

這段代碼會輸出
Hello public java.lang.String org.example.bytebuddy.test.Source.hello(java.lang.String)!

訪問成員變量

使用FieldAccessor可以訪問類成員變量,并且可以讀寫變量的值。

我們可以通過FieldAccessor.ofBeanProperty()來為類構(gòu)建Java Bean規(guī)范的getset方法:

new ByteBuddy()
            .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
            .name("org.example.bytebuddy.FieldTest")
            .defineField("myField", String.class, Visibility.PRIVATE)
            .defineField("myTest", String.class, Visibility.PRIVATE)
            .defineMethod("getMyField", String.class)
            .intercept(FieldAccessor.ofBeanProperty())
            .make()
            .saveIn(new File("result"));

當然如果需要自行定義field的綁定名稱,可以通過FieldAccessor.ofField來指定:

new ByteBuddy()
            .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
            .name("org.example.bytebuddy.FieldTest")
            .defineField("myField", String.class, Visibility.PRIVATE)
            .defineField("myTest", String.class, Visibility.PRIVATE)
            .defineMethod("getMyField", String.class)
            .intercept(FieldAccessor.ofField("myTest"))
            .make()
            .saveIn(new File("result"));

總結(jié)

ByteBuddy作為一種高性能的字節(jié)碼組件有著較為廣泛的使用。他的能力非常強大,此處只是介紹了他的部分能力,如果有需要的話可以前往byte-buddy了解更多信息。

以上就是Java字節(jié)碼ByteBuddy使用及原理解析下的詳細內(nèi)容,更多關(guān)于Java字節(jié)碼ByteBuddy的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

乌拉特前旗| 定陶县| 溧阳市| 武冈市| 高要市| 东阳市| 道真| 叶城县| 韩城市| 浦北县| 根河市| 额敏县| 九江市| 江津市| 丰县| 惠州市| 新闻| 莒南县| 紫金县| 峨眉山市| 峨眉山市| 镇远县| 通化县| 汶川县| 汉川市| 隆回县| 巴彦县| 鄂伦春自治旗| 武清区| 武义县| 成安县| 启东市| 仲巴县| 绥江县| 双桥区| 宝坻区| 灵川县| 鄂尔多斯市| 奉化市| 舟曲县| 东源县|