欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java中Javassist的使用

 更新時(shí)間:2023年04月03日 16:41:52   作者:胡安民  
常用的一些操作字節(jié)碼的技術(shù)有?ASM、AspectJ、Javassist?等。本文主要為大家介紹了Javassist使用的相關(guān)知識(shí),感興趣的小伙伴可以了解一下

開(kāi)篇

說(shuō)起 AOP 小伙伴們肯定很熟悉,無(wú)論是 JDK 動(dòng)態(tài)代理或者是 CGLIB 等,其底層都是通過(guò)操作 Java 字節(jié)碼來(lái)實(shí)現(xiàn)代理。常用的一些操作字節(jié)碼的技術(shù)有 ASM、AspectJ、Javassist 等。

ASM 其設(shè)計(jì)和實(shí)現(xiàn)是盡可能小而且快,更專注于性能。它在指令的層面來(lái)操作,所以使用它需要對(duì) JVM 的指令有所了解,門檻較高,CGLIB 就使用了 ASM 技術(shù)。

AspectJ 擴(kuò)展了 Java 語(yǔ)言,定義了一系列 AOP 語(yǔ)法,在 JVM 中運(yùn)行需要使用特定的編譯器生成遵守 Java 字節(jié)碼規(guī)范的 Class 文件,Spring AOP 使用了 AspectJ 。

Javassist 直接使用 Java 編碼的形式操作字節(jié)碼,簡(jiǎn)單易上手,性能高于反射,相比于 ASM 稍低。

Javassist 常用類

Javassist 抽象出一個(gè) ClassPool 對(duì)象來(lái)操作 Java 類,可以通過(guò) ClassPool.getDefault() 來(lái)獲取默認(rèn)的 ClassPool 。常用的對(duì)象:

CtClass:代表一個(gè) Class 的實(shí)例,可以通過(guò)類的全限定名來(lái)獲取 CtClass 對(duì)象,其中包含了對(duì) Class 的各種操作。

ClassPool:通過(guò) HashTable 保存了路徑下的 CtClass 信息,key為類的全限定名稱,value 為類名對(duì)應(yīng)的 CtClass 對(duì)象。

CtMethod、CtField:抽象出類的方法和屬性,可以用于定義或修改方法和字段。

Javassist 的使用

依賴

<dependency>
  <groupId>org.javassist</groupId>
  <artifactId>javassist</artifactId>
  <version>3.27.0-GA</version>
</dependency>

代碼示例

// 獲取默認(rèn)類池
  ClassPool classPool = ClassPool.getDefault();
  // 1. 創(chuàng)建空類
  CtClass ctClass = classPool.makeClass("com.aysaml.demo.javassist.User");

  // 2. 創(chuàng)建 String 類型的 name 字段
  CtField field = new CtField(classPool.get("java.lang.String"), "name", ctClass);
  // 設(shè)置字段訪問(wèn)級(jí)別 private
  field.setModifiers(Modifier.PRIVATE);
  // 增加字段
  ctClass.addField(field);

  // 3. 增加 getter & setter 方法
  ctClass.addMethod(CtNewMethod.getter("getName", field));
  ctClass.addMethod(CtNewMethod.setter("setName", field));

  // 4. 增加無(wú)參構(gòu)造方法:其中 $0 表示 this,$1 表示參數(shù)
  CtConstructor noArgsCons = new CtConstructor(new CtClass[] {}, ctClass);
  noArgsCons.setBody("{$0.name=\"mark\";}");
  ctClass.addConstructor(noArgsCons);

  // 5. 增加有參構(gòu)造方法
  CtConstructor hasArgsCons =
    new CtConstructor(new CtClass[] {classPool.get("java.lang.String")}, ctClass);
  hasArgsCons.setBody("{$0.name=$1;}");
  ctClass.addConstructor(hasArgsCons);

  // 6. 創(chuàng)建方法
  CtMethod method = new CtMethod(CtClass.voidType, "printName", new CtClass[] {}, ctClass);
  method.setBody("{System.out.println($0.name);}");
  ctClass.addMethod(method);

  // 7. 生成類文件:可指定路徑,默認(rèn)為當(dāng)前項(xiàng)目根目錄
  ctClass.writeFile();

  // 8. 創(chuàng)建類實(shí)例
  Object person = ctClass.toClass().newInstance();

如何實(shí)現(xiàn)類似 AOP 的功能

javassist 對(duì)于編程化的操作字節(jié)碼是很簡(jiǎn)單易懂的,我們以在方法的開(kāi)頭結(jié)尾打印信息為例:

public class Cat {

 /** 記錄喵喵喵的次數(shù) */
 private int num;

 public void miao() {
  this.num++;
 }
}

我們要在 miao( ) 方法的前增加聲音輸出:

public static void main(String[] args) throws NotFoundException, CannotCompileException {
  ClassPool classPool = ClassPool.getDefault();
  // 獲取 Cat 類的 CtClass 對(duì)象
  CtClass catClass = classPool.get("com.aysaml.demo.javassist.Cat");
  // 獲取 miao( ) 方法
  CtMethod method = catClass.getDeclaredMethod("miao");
  method.insertBefore("System.out.println(\"miao~\");");
  // 加載修改過(guò)的類,注意必須要保證調(diào)用前這個(gè)類沒(méi)有被加載過(guò)
  catClass.toClass();
  //測(cè)試
  Cat cat = new Cat();
  cat.miao();
 }

注意到,在使用 catClass.toClass() 加載被修改過(guò)的類時(shí),強(qiáng)調(diào)必須保證在調(diào)用前這個(gè)類沒(méi)有被加載過(guò),否則會(huì)報(bào) attempted duplicate class definition for name 異常。

我們知道一個(gè)類是不能被一個(gè)類加載器加載兩次的,所以為了解決這個(gè)問(wèn)題,需要制定一個(gè)沒(méi)有加載過(guò)該類的 Classloader,Javassist 提供了一個(gè) ClassLoader ,如下:

public class Cat {

 /** 記錄喵喵喵的次數(shù) */
 private int num;

 public void miao() {
  System.out.println("調(diào)用了 miao 方法");
  this.num++;
 }

 public static void main(String[] args) throws Exception{
  ClassPool classPool = ClassPool.getDefault();
  // 獲取 Cat 類的 CtClass 對(duì)象
  CtClass catClass = classPool.get("com.aysaml.demo.javassist.Cat");
  // 獲取 miao( ) 方法
  CtMethod method = catClass.getDeclaredMethod("miao");
  method.insertBefore("System.out.println(\"miao~\");");
  // 重新設(shè)置一個(gè) Classloader
  Loader classLoader = new Loader(classPool);
  Class clazz = classLoader.loadClass("com.aysaml.demo.javassist.Cat");
  // 調(diào)用修改過(guò)的類的方法
  clazz.getDeclaredMethod("miao").invoke(clazz.newInstance());
 }
}

執(zhí)行結(jié)果為:

到此這篇關(guān)于詳解Java中Javassist的使用的文章就介紹到這了,更多相關(guān)Java Javassist內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Springboot實(shí)現(xiàn)word在線編輯保存

    使用Springboot實(shí)現(xiàn)word在線編輯保存

    PageOffice目前支持的Web編程語(yǔ)言及架構(gòu)有:Java(JSP、SSH、MVC等),ASP.NET(C#、VB.NET、MVC、Razor等),PHP,ASP,本篇文章就帶你使用Springboot整合PageOffice實(shí)現(xiàn)word在線編輯保存
    2021-08-08
  • Java使用JDBC連接數(shù)據(jù)庫(kù)

    Java使用JDBC連接數(shù)據(jù)庫(kù)

    本文詳細(xì)講解了Java使用JDBC連接數(shù)據(jù)庫(kù),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • SpringBoot多線程與任務(wù)調(diào)度總結(jié)

    SpringBoot多線程與任務(wù)調(diào)度總結(jié)

    多線程與任務(wù)調(diào)度是java開(kāi)發(fā)中必須掌握的技能,本文主要介紹了SpringBoot多線程與任務(wù)調(diào)度總結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • RabbitMQ消息的延遲隊(duì)列詳解

    RabbitMQ消息的延遲隊(duì)列詳解

    這篇文章主要介紹了RabbitMQ消息的延遲隊(duì)列,延遲隊(duì)列也就是死信交換機(jī),有些隊(duì)列的消息成為死信后,消息中間件可以將其從當(dāng)前隊(duì)列發(fā)送到另一個(gè)隊(duì)列中,這個(gè)隊(duì)列就是死信隊(duì)列,感興趣的同學(xué)可以參考下文
    2024-02-02
  • 淺談SpringBoot Bean加載優(yōu)先級(jí)的問(wèn)題

    淺談SpringBoot Bean加載優(yōu)先級(jí)的問(wèn)題

    這篇文章主要介紹了淺談SpringBoot Bean加載優(yōu)先級(jí)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Spring中Bean的命名方式代碼詳解

    Spring中Bean的命名方式代碼詳解

    這篇文章主要介紹了Spring中Bean的命名方式代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • 8個(gè)簡(jiǎn)單部分開(kāi)啟Java語(yǔ)言學(xué)習(xí)之路 附j(luò)ava學(xué)習(xí)書單

    8個(gè)簡(jiǎn)單部分開(kāi)啟Java語(yǔ)言學(xué)習(xí)之路 附j(luò)ava學(xué)習(xí)書單

    8個(gè)簡(jiǎn)單部分開(kāi)啟Java語(yǔ)言學(xué)習(xí)之路,附j(luò)ava學(xué)習(xí)書單,這篇文章主要向大家介紹了學(xué)習(xí)java語(yǔ)言的方向,感興趣的小伙伴們可以參考一下
    2016-09-09
  • SpringMVC 單文件,多文件上傳實(shí)現(xiàn)詳解

    SpringMVC 單文件,多文件上傳實(shí)現(xiàn)詳解

    這篇文章主要介紹了SpringMVC 單文件,多文件上傳實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 利用注解配置Spring容器的方法

    利用注解配置Spring容器的方法

    本篇文章主要介紹了利用注解配置Spring容器的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • Spring Boot超詳細(xì)分析啟動(dòng)流程

    Spring Boot超詳細(xì)分析啟動(dòng)流程

    SpringBoot是Spring開(kāi)源組織下的子項(xiàng)目,是Spring組件一站式解決方案,主要是簡(jiǎn)化了使用Spring的難度,簡(jiǎn)省了繁重的配置,提供了各種啟動(dòng)器,開(kāi)發(fā)者能快速上手,這篇文章主要給大家介紹了關(guān)于Spring Boot啟動(dòng)流程知識(shí)點(diǎn)的相關(guān)資料,需要的朋友可以參考下
    2022-07-07

最新評(píng)論