日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ClassPool CtClass浅析

發(fā)布時(shí)間:2025/3/21 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ClassPool CtClass浅析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最近在看android中的熱更新原理,里面有用到j(luò)avassist來(lái)更改.class,因而又惡補(bǔ)了下ClassPool和CtClass的相關(guān)使用。雖然android中現(xiàn)在熱更新是用?groovy?groovy和java語(yǔ)法很類似,所以先弄java版的~

什么是javassist

Javassit是一個(gè)處理Java字節(jié)碼的類庫(kù)。Java字節(jié)碼存儲(chǔ)在名叫class file的二進(jìn)制文件里。每個(gè)class文件包含一個(gè)Java類或者接口。Javassit.CtClass是一個(gè)class文件的抽象表示。一個(gè)CtClass(compile-time class)對(duì)象可以用來(lái)處理一個(gè)class文件。

通過(guò)javassist生成.class文件

public static void main(String[] args) {//默認(rèn)的類搜索路徑ClassPool pool = ClassPool.getDefault();//獲取一個(gè)ctClass對(duì)象CtClass ctClass = pool.makeClass("com.luoxiaohui.Test");try {//添加age屬性ctClass.addField(CtField.make("private int age;", ctClass));//添加setAge方法ctClass.addMethod(CtMethod.make("public void setAge(int age){this.age = age;}", ctClass));//添加getAge方法ctClass.addMethod(CtMethod.make("public int getAge(){return this.age;}", ctClass));//將ctClass生成字節(jié)數(shù)組,并寫入文件byte[] byteArray = ctClass.toBytecode();FileOutputStream output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray); output.close();System.out.println("文件寫入成功!!!");} catch (Exception e) {e.printStackTrace();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

可以看到相應(yīng)目錄下生成了Test.class文件,然后通過(guò)JD-GUI工具打開,如圖所示:?
?
可以看到,屬性和兩個(gè)方法,都已經(jīng)寫入到.class文件中,OK啦!

如何修改已經(jīng)被JVM加載的.class文件

模擬被JVM加載的.class文件代碼

ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.luoxiaohui.Test");try {//添加屬性ctClass.addField(CtField.make("private int age;", ctClass));//添加setAge方法ctClass.addMethod(CtMethod.make("public void setAge(int age){this.age = age;}", ctClass));ctClass.addMethod(CtMethod.make("public int getAge(){return this.age;}", ctClass));byte[] byteArray = ctClass.toBytecode();FileOutputStream output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray); output.close();System.out.println("文件生成成功!!!");//這里用pool.get()去獲取ctClass對(duì)象,表示默認(rèn)JVM已經(jīng)加載此類.ctClass = pool.get("com.luoxiaohui.Test");ctClass.addField(CtField.make("private String sex;", ctClass));ctClass.addField(CtField.make("private String name;", ctClass));byteArray = ctClass.toBytecode();output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray);output.close();System.out.println("文件修改成功!!!!");} catch (Exception e) {e.printStackTrace();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

發(fā)現(xiàn)報(bào)錯(cuò),log如下所示:?
?
報(bào)錯(cuò)位置在

ctClass.addField(CtField.make("private String sex;", ctClass));
  • 1

凍結(jié)class原因

如果一個(gè)CtClass對(duì)象通過(guò)writeFile(),toClass()或者toBytecode()轉(zhuǎn)換成了class文件,那么Javassist會(huì)凍結(jié)這個(gè)CtClass對(duì)象。后面就不能繼續(xù)修改這個(gè)CtClass對(duì)象了。這樣是為了警告開發(fā)者不要修改已經(jīng)被JVM加載的class文件,因?yàn)镴VM不允許重新加載一個(gè)類。

然后我在調(diào)用pool.get()之前,先調(diào)用代碼:

if(ctClass.isFrozen()){ctClass.defrost(); }
  • 1
  • 2
  • 3

運(yùn)行代碼,結(jié)果還是會(huì)報(bào)錯(cuò),log如圖所示:?

被精簡(jiǎn)原因

如果ClassPool.doPruning被設(shè)置成true,那么Javassist會(huì)在凍結(jié)一個(gè)對(duì)象的時(shí)候?qū)@個(gè)對(duì)象進(jìn)行精簡(jiǎn)。為了減少ClassPool的內(nèi)存占用,精簡(jiǎn)的時(shí)候會(huì)丟棄class中不需要的屬性。例如Code_attribute結(jié)構(gòu)(即是方法體)會(huì)被丟棄。因此,如果一個(gè)CtClass對(duì)象被精簡(jiǎn)了,那么方法的字節(jié)碼是不能訪問(wèn)的,留下的只有方法名,方法的簽名和annotation。被精簡(jiǎn)的CtClass對(duì)象不能夠再被defrost。ClassPool.doPruning的默認(rèn)值是true。?
所以,如果要阻止對(duì)某一個(gè)特定的CtClass對(duì)象的精簡(jiǎn),即需要修改某個(gè).class文件,需要在這個(gè)CtClass對(duì)象上先調(diào)用stopPruing()方法:

ctClass.stopPruning(true);
  • 1

完整代碼

完整代碼如下所示:

ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.luoxiaohui.Test"); ctClass.stopPruning(true);try {//添加屬性ctClass.addField(CtField.make("private int age;", ctClass));//添加setAge方法ctClass.addMethod(CtMethod.make("public void setAge(int age){this.age = age;}", ctClass));ctClass.addMethod(CtMethod.make("public int getAge(){return this.age;}", ctClass));byte[] byteArray = ctClass.toBytecode();FileOutputStream output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray); output.close();System.out.println("文件寫入成功!!!");if(ctClass.isFrozen()){ctClass.defrost();}ctClass = pool.get("com.luoxiaohui.Test");ctClass.addField(CtField.make("private String sex;", ctClass));ctClass.addField(CtField.make("private String name;", ctClass));byteArray = ctClass.toBytecode();output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray);output.close();System.out.println("文件修改成功!!!!");} catch (Exception e) {e.printStackTrace();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

參考博客:http://blog.chinaunix.net/uid-21718047-id-3342374.html


from:?https://blog.csdn.net/a394268045/article/details/51996082

總結(jié)

以上是生活随笔為你收集整理的ClassPool CtClass浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。