ClassPool CtClass浅析
最近在看android中的熱更新原理,里面有用到j(luò)avassist來(lái)更改.class,因而又惡補(bǔ)了下ClassPool和CtClass的相關(guān)使用。雖然android中現(xiàn)在熱更新是用?groovy,?groovy和java語(yǔ)法很類(lèi)似,所以先弄java版的~
什么是javassist
Javassit是一個(gè)處理Java字節(jié)碼的類(lèi)庫(kù)。Java字節(jié)碼存儲(chǔ)在名叫class file的二進(jìn)制文件里。每個(gè)class文件包含一個(gè)Java類(lèi)或者接口。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)的類(lèi)搜索路徑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ù)組,并寫(xiě)入文件byte[] byteArray = ctClass.toBytecode();FileOutputStream output = new FileOutputStream("/Users/luoxiaohui/Desktop/test/Test.class");output.write(byteArray); output.close();System.out.println("文件寫(xiě)入成功!!!");} 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工具打開(kāi),如圖所示:?
?
可以看到,屬性和兩個(gè)方法,都已經(jīng)寫(xiě)入到.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)加載此類(lèi).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ò)位置在
- 1
凍結(jié)class原因
如果一個(gè)CtClass對(duì)象通過(guò)writeFile(),toClass()或者toBytecode()轉(zhuǎn)換成了class文件,那么Javassist會(huì)凍結(jié)這個(gè)CtClass對(duì)象。后面就不能繼續(xù)修改這個(gè)CtClass對(duì)象了。這樣是為了警告開(kāi)發(fā)者不要修改已經(jīng)被JVM加載的class文件,因?yàn)镴VM不允許重新加載一個(gè)類(lèi)。
然后我在調(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é)碼是不能訪(fǎng)問(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()方法:
- 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("文件寫(xiě)入成功!!!");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)題。
- 上一篇: Spring 实践:AOP
- 下一篇: JavaSist之ClassPool