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

javassist使用指南

 更新時(shí)間:2020年07月01日 11:53:50   作者:rickiyang  
這篇文章主要介紹了javassist的使用方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

Java 字節(jié)碼以二進(jìn)制的形式存儲(chǔ)在 .class 文件中,每一個(gè) .class 文件包含一個(gè) Java 類(lèi)或接口。Javaassist 就是一個(gè)用來(lái) 處理 Java 字節(jié)碼的類(lèi)庫(kù)。它可以在一個(gè)已經(jīng)編譯好的類(lèi)中添加新的方法,或者是修改已有的方法,并且不需要對(duì)字節(jié)碼方面有深入的了解。同時(shí)也可以去生成一個(gè)新的類(lèi)對(duì)象,通過(guò)完全手動(dòng)的方式。

1. 使用 Javassist 創(chuàng)建一個(gè) class 文件

首先需要引入jar包:

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

編寫(xiě)創(chuàng)建對(duì)象的類(lèi):

package com.rickiyang.learn.javassist;

import javassist.*;

/**
 * @author rickiyang
 * @date 2019-08-06
 * @Desc
 */
public class CreatePerson {

 /**
 * 創(chuàng)建一個(gè)Person 對(duì)象
 *
 * @throws Exception
 */
 public static void createPseson() throws Exception {
 ClassPool pool = ClassPool.getDefault();

 // 1. 創(chuàng)建一個(gè)空類(lèi)
 CtClass cc = pool.makeClass("com.rickiyang.learn.javassist.Person");

 // 2. 新增一個(gè)字段 private String name;
 // 字段名為name
 CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
 // 訪問(wèn)級(jí)別是 private
 param.setModifiers(Modifier.PRIVATE);
 // 初始值是 "xiaoming"
 cc.addField(param, CtField.Initializer.constant("xiaoming"));

 // 3. 生成 getter、setter 方法
 cc.addMethod(CtNewMethod.setter("setName", param));
 cc.addMethod(CtNewMethod.getter("getName", param));

 // 4. 添加無(wú)參的構(gòu)造函數(shù)
 CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
 cons.setBody("{name = \"xiaohong\";}");
 cc.addConstructor(cons);

 // 5. 添加有參的構(gòu)造函數(shù)
 cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
 // $0=this / $1,$2,$3... 代表方法參數(shù)
 cons.setBody("{$0.name = $1;}");
 cc.addConstructor(cons);

 // 6. 創(chuàng)建一個(gè)名為printName方法,無(wú)參數(shù),無(wú)返回值,輸出name值
 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
 ctMethod.setModifiers(Modifier.PUBLIC);
 ctMethod.setBody("{System.out.println(name);}");
 cc.addMethod(ctMethod);

 //這里會(huì)將這個(gè)創(chuàng)建的類(lèi)對(duì)象編譯為.class文件
 cc.writeFile("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
 }

 public static void main(String[] args) {
 try {
  createPseson();
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
}

執(zhí)行上面的 main 函數(shù)之后,會(huì)在指定的目錄內(nèi)生成 Person.class 文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.rickiyang.learn.javassist;

public class Person {
 private String name = "xiaoming";

 public void setName(String var1) {
 this.name = var1;
 }

 public String getName() {
 return this.name;
 }

 public Person() {
 this.name = "xiaohong";
 }

 public Person(String var1) {
 this.name = var1;
 }

 public void printName() {
 System.out.println(this.name);
 }
}

跟咱們預(yù)想的一樣。

在 Javassist 中,類(lèi) Javaassit.CtClass 表示 class 文件。一個(gè) GtClass (編譯時(shí)類(lèi))對(duì)象可以處理一個(gè) class 文件,ClassPoolCtClass 對(duì)象的容器。它按需讀取類(lèi)文件來(lái)構(gòu)造 CtClass 對(duì)象,并且保存 CtClass 對(duì)象以便以后使用。

需要注意的是 ClassPool 會(huì)在內(nèi)存中維護(hù)所有被它創(chuàng)建過(guò)的 CtClass,當(dāng) CtClass 數(shù)量過(guò)多時(shí),會(huì)占用大量的內(nèi)存,API中給出的解決方案是 有意識(shí)的調(diào)用CtClassdetach()方法以釋放內(nèi)存。

ClassPool需要關(guān)注的方法:

  1. getDefault : 返回默認(rèn)的ClassPool 是單例模式的,一般通過(guò)該方法創(chuàng)建我們的ClassPool;
  2. appendClassPath, insertClassPath : 將一個(gè)ClassPath加到類(lèi)搜索路徑的末尾位置 或 插入到起始位置。通常通過(guò)該方法寫(xiě)入額外的類(lèi)搜索路徑,以解決多個(gè)類(lèi)加載器環(huán)境中找不到類(lèi)的尷尬;
  3. toClass : 將修改后的CtClass加載至當(dāng)前線程的上下文類(lèi)加載器中,CtClass的toClass方法是通過(guò)調(diào)用本方法實(shí)現(xiàn)。需要注意的是一旦調(diào)用該方法,則無(wú)法繼續(xù)修改已經(jīng)被加載的class;
  4. get , getCtClass : 根據(jù)類(lèi)路徑名獲取該類(lèi)的CtClass對(duì)象,用于后續(xù)的編輯。

CtClass需要關(guān)注的方法:

  1. freeze : 凍結(jié)一個(gè)類(lèi),使其不可修改;
  2. isFrozen : 判斷一個(gè)類(lèi)是否已被凍結(jié);
  3. prune : 刪除類(lèi)不必要的屬性,以減少內(nèi)存占用。調(diào)用該方法后,許多方法無(wú)法將無(wú)法正常使用,慎用;
  4. defrost : 解凍一個(gè)類(lèi),使其可以被修改。如果事先知道一個(gè)類(lèi)會(huì)被defrost, 則禁止調(diào)用 prune 方法;
  5. detach : 將該class從ClassPool中刪除;
  6. writeFile : 根據(jù)CtClass生成 .class 文件;
  7. toClass : 通過(guò)類(lèi)加載器加載該CtClass。

上面我們創(chuàng)建一個(gè)新的方法使用了CtMethod類(lèi)。CtMthod代表類(lèi)中的某個(gè)方法,可以通過(guò)CtClass提供的API獲取或者CtNewMethod新建,通過(guò)CtMethod對(duì)象可以實(shí)現(xiàn)對(duì)方法的修改。

CtMethod中的一些重要方法:

  1. insertBefore : 在方法的起始位置插入代碼;
  2. insterAfter : 在方法的所有 return 語(yǔ)句前插入代碼以確保語(yǔ)句能夠被執(zhí)行,除非遇到exception;
  3. insertAt : 在指定的位置插入代碼;
  4. setBody : 將方法的內(nèi)容設(shè)置為要寫(xiě)入的代碼,當(dāng)方法被 abstract修飾時(shí),該修飾符被移除;
  5. make : 創(chuàng)建一個(gè)新的方法。

注意到在上面代碼中的:setBody()的時(shí)候我們使用了一些符號(hào):

// $0=this / $1,$2,$3... 代表方法參數(shù)
cons.setBody("{$0.name = $1;}");

具體還有很多的符號(hào)可以使用,但是不同符號(hào)在不同的場(chǎng)景下會(huì)有不同的含義,所以在這里就不在贅述,可以看javassist 的說(shuō)明文檔。http://www.javassist.org/tutorial/tutorial2.html

2. 調(diào)用生成的類(lèi)對(duì)象

(1). 通過(guò)反射的方式調(diào)用

上面的案例是創(chuàng)建一個(gè)類(lèi)對(duì)象然后輸出該對(duì)象編譯完之后的 .class 文件。那如果我們想調(diào)用生成的類(lèi)對(duì)象中的屬性或者方法應(yīng)該怎么去做呢?javassist也提供了相應(yīng)的api,生成類(lèi)對(duì)象的代碼還是和第一段一樣,將最后寫(xiě)入文件的代碼替換為如下:

// 這里不寫(xiě)入文件,直接實(shí)例化
Object person = cc.toClass().newInstance();
// 設(shè)置值
Method setName = person.getClass().getMethod("setName", String.class);
setName.invoke(person, "cunhua");
// 輸出值
Method execute = person.getClass().getMethod("printName");
execute.invoke(person);

然后執(zhí)行main方法就可以看到調(diào)用了 printName方法。

(2). 通過(guò)讀取 .class 文件的方式調(diào)用

ClassPool pool = ClassPool.getDefault();
// 設(shè)置類(lèi)路徑
pool.appendClassPath("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
CtClass ctClass = pool.get("com.rickiyang.learn.javassist.Person");
Object person = ctClass.toClass().newInstance();
// ...... 下面和通過(guò)反射的方式一樣去使用

(3). 通過(guò)接口的方式

上面兩種其實(shí)都是通過(guò)反射的方式去調(diào)用,問(wèn)題在于我們的工程中其實(shí)并沒(méi)有這個(gè)類(lèi)對(duì)象,所以反射的方式比較麻煩,并且開(kāi)銷(xiāo)也很大。那么如果你的類(lèi)對(duì)象可以抽象為一些方法得合集,就可以考慮為該類(lèi)生成一個(gè)接口類(lèi)。這樣在newInstance()的時(shí)候我們就可以強(qiáng)轉(zhuǎn)為接口,可以將反射的那一套省略掉了。

還拿上面的Person類(lèi)來(lái)說(shuō),新建一個(gè)PersonI接口類(lèi):

package com.rickiyang.learn.javassist;

/**
 * @author rickiyang
 * @date 2019-08-07
 * @Desc
 */
public interface PersonI {

 void setName(String name);

 String getName();

 void printName();

}

實(shí)現(xiàn)部分的代碼如下:

ClassPool pool = ClassPool.getDefault();
pool.appendClassPath("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");

// 獲取接口
CtClass codeClassI = pool.get("com.rickiyang.learn.javassist.PersonI");
// 獲取上面生成的類(lèi)
CtClass ctClass = pool.get("com.rickiyang.learn.javassist.Person");
// 使代碼生成的類(lèi),實(shí)現(xiàn) PersonI 接口
ctClass.setInterfaces(new CtClass[]{codeClassI});

// 以下通過(guò)接口直接調(diào)用 強(qiáng)轉(zhuǎn)
PersonI person = (PersonI)ctClass.toClass().newInstance();
System.out.println(person.getName());
person.setName("xiaolv");
person.printName();

使用起來(lái)很輕松。

3. 修改現(xiàn)有的類(lèi)對(duì)象

前面說(shuō)到新增一個(gè)類(lèi)對(duì)象。這個(gè)使用場(chǎng)景目前還沒(méi)有遇到過(guò),一般會(huì)遇到的使用場(chǎng)景應(yīng)該是修改已有的類(lèi)。比如常見(jiàn)的日志切面,權(quán)限切面。我們利用javassist來(lái)實(shí)現(xiàn)這個(gè)功能。

有如下類(lèi)對(duì)象:

package com.rickiyang.learn.javassist;

/**
 * @author rickiyang
 * @date 2019-08-07
 * @Desc
 */
public class PersonService {

 public void getPerson(){
 System.out.println("get Person");
 }

 public void personFly(){
 System.out.println("oh my god,I can fly");
 }
}

然后對(duì)他進(jìn)行修改:

package com.rickiyang.learn.javassist;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;

import java.lang.reflect.Method;

/**
 * @author rickiyang
 * @date 2019-08-07
 * @Desc
 */
public class UpdatePerson {

 public static void update() throws Exception {
 ClassPool pool = ClassPool.getDefault();
 CtClass cc = pool.get("com.rickiyang.learn.javassist.PersonService");

 CtMethod personFly = cc.getDeclaredMethod("personFly");
 personFly.insertBefore("System.out.println(\"起飛之前準(zhǔn)備降落傘\");");
 personFly.insertAfter("System.out.println(\"成功落地。。。。\");");


 //新增一個(gè)方法
 CtMethod ctMethod = new CtMethod(CtClass.voidType, "joinFriend", new CtClass[]{}, cc);
 ctMethod.setModifiers(Modifier.PUBLIC);
 ctMethod.setBody("{System.out.println(\"i want to be your friend\");}");
 cc.addMethod(ctMethod);

 Object person = cc.toClass().newInstance();
 // 調(diào)用 personFly 方法
 Method personFlyMethod = person.getClass().getMethod("personFly");
 personFlyMethod.invoke(person);
 //調(diào)用 joinFriend 方法
 Method execute = person.getClass().getMethod("joinFriend");
 execute.invoke(person);
 }

 public static void main(String[] args) {
 try {
  update();
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
}

personFly方法前后加上了打印日志。然后新增了一個(gè)方法joinFriend。執(zhí)行main函數(shù)可以發(fā)現(xiàn)已經(jīng)添加上了。

另外需要注意的是:上面的insertBefore() setBody()中的語(yǔ)句,如果你是單行語(yǔ)句可以直接用雙引號(hào),但是有多行語(yǔ)句的情況下,你需要將多行語(yǔ)句用{}括起來(lái)。javassist只接受單個(gè)語(yǔ)句或用大括號(hào)括起來(lái)的語(yǔ)句塊。

以上就是javassist使用指南的詳細(xì)內(nèi)容,更多關(guān)于javassist使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java 實(shí)現(xiàn)鏈表結(jié)點(diǎn)插入

    Java 實(shí)現(xiàn)鏈表結(jié)點(diǎn)插入

    這篇文章主要介紹了Java 實(shí)現(xiàn)鏈表結(jié)點(diǎn)插入操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Java比較問(wèn)題詳細(xì)分析

    Java比較問(wèn)題詳細(xì)分析

    本篇文章主要給大家講解了Java中比較問(wèn)題的相關(guān)知識(shí),一起參考學(xué)習(xí)下吧。
    2017-12-12
  • Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié)

    Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié)

    Feign 的英文表意為“假裝,偽裝,變形”, 是一個(gè)http請(qǐng)求調(diào)用的輕量級(jí)框架,可以以Java接口注解的方式調(diào)用Http請(qǐng)求,而不用像Java中通過(guò)封裝HTTP請(qǐng)求報(bào)文的方式直接調(diào)用。接下來(lái)通過(guò)本文給大家分享Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié),感興趣的朋友一起看看吧
    2021-05-05
  • springboot基于過(guò)濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作

    springboot基于過(guò)濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作

    這篇文章主要介紹了springboot基于過(guò)濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java IO流對(duì)文件File操作

    Java IO流對(duì)文件File操作

    這篇文章主要介紹了Java IO流對(duì)文件File操作,java封裝的一個(gè)操作文件及文件夾(目錄)的對(duì)象??梢圆僮鞔疟P(pán)上的任何一個(gè)文件和文件夾
    2022-12-12
  • 一文詳解JavaWeb過(guò)濾器(Filter)

    一文詳解JavaWeb過(guò)濾器(Filter)

    本文主要介紹了一文詳解JavaWeb過(guò)濾器(Filter),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • java.lang.Instrument 代理Agent使用詳細(xì)介紹

    java.lang.Instrument 代理Agent使用詳細(xì)介紹

    這篇文章主要介紹了java.lang.Instrument 代理Agent使用詳細(xì)介紹的相關(guān)資料,附有實(shí)例代碼,幫助大家學(xué)習(xí)參考,需要的朋友可以參考下
    2016-11-11
  • 使用BigDecimal進(jìn)行精確運(yùn)算(實(shí)現(xiàn)加減乘除運(yùn)算)

    使用BigDecimal進(jìn)行精確運(yùn)算(實(shí)現(xiàn)加減乘除運(yùn)算)

    這篇文章主要介紹了如何使用BigDecimal進(jìn)行精確運(yùn)算,最后提供了一個(gè)工具類(lèi),該工具類(lèi)提供加,減,乘,除運(yùn)算
    2013-11-11
  • Java線程優(yōu)先級(jí)示例代碼

    Java線程優(yōu)先級(jí)示例代碼

    使用過(guò)Bit下載軟件的同學(xué)應(yīng)該很清楚,我們有多個(gè)下載任務(wù)同時(shí)執(zhí)行,而其中的某一個(gè)或多個(gè)是非常重要的,于是給這些任務(wù)設(shè)定一個(gè)高度優(yōu)先,以便任務(wù)可以獲取更多的帶寬盡早完成下載
    2013-09-09
  • Zuul 如何屏蔽服務(wù)和指定路徑

    Zuul 如何屏蔽服務(wù)和指定路徑

    這篇文章主要介紹了Zuul 如何屏蔽服務(wù)和指定路徑的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論