亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

什么是ByteBuddy

ByteBuddy是一個java的運行時代碼生成庫,他可以幫助你以字節(jié)碼的方式動態(tài)修改java類的代碼。

為什么需要ByteBuddy

Java是一個強類型語言,有著極為嚴格的類型系統(tǒng)。這個嚴格的類型系統(tǒng)可以幫助構(gòu)建嚴謹,更不容易被腐化的代碼,但是也在某些方面限制了java的應(yīng)用。不過為了解決這個問題,java提供了一套反射的api來幫助使用者感知和修改類的內(nèi)部。

不過反射也有他的缺點:

  • 反射顯而易見的缺點是慢。我們在使用反射之前都需要謹慎的考慮他對于當前性能的影響,唯有進過詳細的評估,才能夠放心的使用。
  • 反射能夠繞過類型安全檢查。我們在使用反射的時候需要確保相應(yīng)的接口不會暴露給外部用戶,不然可能造成不小的安全隱患。

ByteBuddy就可以幫助我們做到反射能做的事情,而不必受困于他的這些缺點。

ByteBuddy使用

創(chuàng)建一個類

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

上述代碼創(chuàng)建了一個Object的子類并且創(chuàng)建了toString方法輸出Hello World!通過找到保存的輸出類我們可以看到最后的類是這樣的:

package net.bytebuddy.renamed.java.lang;
public class Object$ByteBuddy$tPSTnhZh {
    public String toString() {
        return "Hello World!";
    }
    public Object$ByteBuddy$tPSTnhZh() {
    }
}

可以看到我們雖然創(chuàng)建了一個類,但是我們沒有為這個類取名,通過結(jié)果得知最后的類名是
net.bytebuddy.renamed.java.lang.Object$ByteBuddy$tPSTnhZh,那么這個類名是怎么來的呢?

在ByteBuddy中如果沒有指定類名,他會調(diào)用默認的NamingStrategy策略來生成類名,一般情況下為

父類的全限定名 + $ByteBuddy$ + 隨機字符串
例如: org.example.MyTest$ByteBuddy$NsT9pB6w

如果父類是java.lang目錄下的類,例如Object,那么會變成

net.bytebuddy.renamed. + 父類的全限定名 + $ByteBuddy$ + 隨機字符串
例如: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$2VOeD4Lh

以此來規(guī)避java安全模型的限制。

類型重定義與變基

定義一個類

package org.example.bytebuddy.test;
public class MyClassTest {
    public String test() {
        return "my test";
    }
}

用這個類來驗證如下的能力

類型重定義(type redefinition)

ByteBuddy支持對于已存在的類進行重定義,即可以添加或者刪除類的方法。只不過當類的方法被重定義之后,那么原先的方法中的信息就會丟失。

Class<?> dynamicType = new ByteBuddy()
                .redefine(MyClassTest.class)
                .method(ElementMatchers.named("test"))
                .intercept(FixedValue.value("Hello World!"))
                .make()
                .load(String.class.getClassLoader()).getLoaded();

redefine結(jié)果是

類型變基(type rebasing)

rebase操作和redefinition操作最大的區(qū)別就是rebase操作不會丟失原先的類的方法信息。大致的實現(xiàn)原理是在變基操作的時候把所有的方法實現(xiàn)復制到重新命名的私有方法(具有和原先方法兼容的簽名)中,這樣原先的方法就不會丟失。

Class&lt;?&gt; dynamicType = new ByteBuddy()
                .rebase(MyClassTest.class)
                .method(ElementMatchers.named("test"))
                .intercept(FixedValue.value("Hello World!"))
                .make()
                .load(String.class.getClassLoader()).getLoaded();

rebase之后結(jié)果

可以看到原先的方法被重命名后保留了下來,并且變成了私有方法。

注意redefinition和rebasing不能修改已經(jīng)被jvm加載的類,不然會報錯Class already loaded

類的加載

生成了之后為了在代碼中使用,必須要經(jīng)過load流程。細心的讀者可能已經(jīng)發(fā)現(xiàn)了上文中已經(jīng)使用到了load相關(guān)的方法。

構(gòu)建了具體的動態(tài)類之后,可以選擇使用saveIn將其結(jié)構(gòu)體存儲下來,也可以選擇將它裝載到虛擬機中。在類加載器的選擇中,ByteBuddy提供了幾種選擇放在ClassLoadingStrategy.Default中:

  • WRAPPER:這個策略會創(chuàng)建一個新的ByteArrayClassLoader,并使用傳入的類加載器為父類。
  • WRAPPER_PERSISTENT:該策略和WRAPPER大致一致,只是會將所有的類文件持久化到類加載器中
  • CHILD_FIRST:這個策略是WRAPPER的改版,其中動態(tài)類型的優(yōu)先級會比父類加載器中的同名類高,即在此種情況下不再是類加載器通常的父類優(yōu)先,而是“子類優(yōu)先”
  • CHILD_FIRST_PERSISTENT:該策略和CHILD_FIRST大致一致,只是會將所有的類文件持久化到類加載器中
  • INJECTION:這個策略最為特殊,他不會創(chuàng)建類加載器,而是通過反射的手段將類注入到指定的類加載器之中。這么做的好處是用這種方法注入的類對于類加載器中的其他類具有私有權(quán)限,而其他的策略不具備這種能力。

類的重載

前面提到過,rebase和redefine通常沒辦法重新加載已經(jīng)存在的類,但是由于jvm的熱替換(HotSwap)機制的存在,使得ByteBuddy可以在加載后也能夠重新定義類。

class Foo {
  String m() { return "foo"; }
}
class Bar {
  String m() { return "bar"; }
}

我們通過ByteBuddy的ClassRelodingsTrategy即可完成熱替換。

ByteBuddyAgent.install();
Foo foo = new Foo();
new ByteBuddy()
  .redefine(Bar.class)
  .name(Foo.class.getName())
  .make()
  .load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

需要注意的是熱替換機制必須依賴Java Agent才能使用。Java Agent是一種可以在java項目運行前或者運行時動態(tài)修改類的技術(shù)。通常可以使用-javaagent參數(shù)引入java agent。

處理尚未加載的類

ByteBuddy除了可以處理已經(jīng)加載完的類,他也具備處理尚未被加載的類的能力。

ByteBuddy對java的反射api做了抽象,例如Class實例就被表示成了TypeDescription實例。事實上,ByteBuddy只知道如何通過實現(xiàn)TypeDescription接口的適配器來處理提供的 Class。這種抽象的一大優(yōu)勢是類信息不需要由類加載器提供,可以由任何其他來源提供。

ByteBuddy中可以通過TypePool獲取類的TypeDescription,ByteBuddy提供了TypePool的默認實現(xiàn)TypePool.Default。這個類可以幫助我們把java字節(jié)碼轉(zhuǎn)換成TypeDescription。

Java的類加載器只會在類第一次使用的時候加載一次,因此我們可以在java中以如下方式安全的創(chuàng)建一個類:

package foo;
class Bar { }

但是通過如下的方法,我們可以在Bar這個類沒有被加載前就提前生成我們自己的Bar,因此后續(xù)jvm就只會使用到我們的Bar

參考文章

[1] https://bytebuddy.net/#/tutorial

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

相關(guān)文章

  • SpringBoot中的Javaconfig代碼示例

    SpringBoot中的Javaconfig代碼示例

    JavaConfig是一種使用Java類替代XML配置文件的方式來定義Spring?Bean的機制,通過使用`@Configuration`和`@Bean`注解,可以將第三方JAR包中的對象納入Spring?IOC容器管理,本文介紹SpringBoot中的Javaconfig,感興趣的朋友一起看看吧
    2025-02-02
  • springmvc—handlermapping三種映射方式

    springmvc—handlermapping三種映射方式

    這篇文章主要介紹了springmvc—handlermapping三種映射方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 解決idea找不到類could not find artifact問題

    解決idea找不到類could not find artifact問題

    本文總結(jié)了解決Java項目中找不到類的問題的常見解決方案,包括刷新Maven項目、清理IDEA緩存、Maven Clean Install、重新Package、解決依賴沖突和手動導入依賴包等方法
    2025-01-01
  • 圖解Spring容器中實例化bean的四種方式

    圖解Spring容器中實例化bean的四種方式

    這篇文章主要介紹了圖解Spring容器中實例化bean的四種方式,傳統(tǒng)應(yīng)用程序可以通過new和反射方式進行實例化Bean,而Spring IOC容器則需要根據(jù) Bean 定義里的配置元數(shù)據(jù),使用反射機制來創(chuàng)建Bean,需要的朋友可以參考下
    2023-11-11
  • mybatis自定義類型處理器的實現(xiàn)

    mybatis自定義類型處理器的實現(xiàn)

    在MyBatis使用中,有時需要對特定數(shù)據(jù)類型進行定制處理,自定義類型處理器(TypeHandler)可以實現(xiàn)這一需求,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-10-10
  • Java騷操作之CountDownLatch代碼詳解

    Java騷操作之CountDownLatch代碼詳解

    這篇文章主要介紹了Java騷操作之CountDownLatch代碼詳解,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • Java的JSTL標簽庫詳解

    Java的JSTL標簽庫詳解

    JSTL包含用于編寫和開發(fā)JSP頁面的一組標準標簽,它可以為用戶提供一個無腳本環(huán)境。在此環(huán)境中,用戶可以使用標簽編寫代碼,而無須使用Java腳本
    2023-05-05
  • 一文徹底吃透SpringMVC中的轉(zhuǎn)發(fā)和重定向

    一文徹底吃透SpringMVC中的轉(zhuǎn)發(fā)和重定向

    大家應(yīng)該都知道springmvc本來就會把返回的字符串作為視圖名解析,然后轉(zhuǎn)發(fā)到對應(yīng)的視圖,這篇文章主要給大家介紹了關(guān)于SpringMVC中轉(zhuǎn)發(fā)和重定向的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-04-04
  • Mybatis實現(xiàn)分包定義數(shù)據(jù)庫的原理與過程

    Mybatis實現(xiàn)分包定義數(shù)據(jù)庫的原理與過程

    這篇文章主要給大家介紹了關(guān)于Mybatis實現(xiàn)分包定義數(shù)據(jù)庫的原理與過程,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-01-01
  • Java Calendar類常用示例_動力節(jié)點Java學院整理

    Java Calendar類常用示例_動力節(jié)點Java學院整理

    從JDK1.1版本開始,在處理日期和時間時,系統(tǒng)推薦使用Calendar類進行實現(xiàn)。接下來通過實例代碼給大家詳細介紹Java Calendar類相關(guān)知識,需要的朋友參考下吧
    2017-04-04

最新評論