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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ClassPool CtClass浅析

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

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

什么是javassist

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

通過javassist生成.class文件

public static void main(String[] args) {//默認的類搜索路徑ClassPool pool = ClassPool.getDefault();//獲取一個ctClass對象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

可以看到相應目錄下生成了Test.class文件,然后通過JD-GUI工具打開,如圖所示:?
?
可以看到,屬性和兩個方法,都已經(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對象,表示默認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)報錯,log如下所示:?
?
報錯位置在

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

凍結(jié)class原因

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

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

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

運行代碼,結(jié)果還是會報錯,log如圖所示:?

被精簡原因

如果ClassPool.doPruning被設置成true,那么Javassist會在凍結(jié)一個對象的時候?qū)@個對象進行精簡。為了減少ClassPool的內(nèi)存占用,精簡的時候會丟棄class中不需要的屬性。例如Code_attribute結(jié)構(gòu)(即是方法體)會被丟棄。因此,如果一個CtClass對象被精簡了,那么方法的字節(jié)碼是不能訪問的,留下的只有方法名,方法的簽名和annotation。被精簡的CtClass對象不能夠再被defrost。ClassPool.doPruning的默認值是true。?
所以,如果要阻止對某一個特定的CtClass對象的精簡,即需要修改某個.class文件,需要在這個CtClass對象上先調(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浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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