JavaSist之ClassPool
ClassPool
ClassPool對象是一個CtClass對象的容器。一個CtClass對象被構建后,它被記錄在ClassPool中。這是因為當編譯的原文件關聯到CtClass表示的類,?編譯器要訪問CtClass對象。
?
??例如,假定一個新方法getter()要加入到CtClass對象表示的的Point類。程序試圖編譯Point中的方法getter()的源代碼,用編譯過的代碼做為方法內容,將它加到另一個類Line中。如果CtClass對象表示的Point丟失了,編譯器將不能編譯getter()方法。注冊初始的類不包含getter()方法。因此,為了正確的編譯一個方法,ClassPool必須擁有程序運行時所有的CtClass實例。
?
避免內存溢出
ClassPool的特點決定了當?CtClass對象數量很多時,它所占的內存會非常大。為了避免這種情況發生,你可以明確的移除一個ClassPool中的不需要的CtClass對象。如果用CtClass的detach()方法,CtClass對象將從ClassPool中移除。例如:
CtClass?cc?=?...?; cc.writeFile(); cc.detach();執行deatach()后,將不能執行CtClass對象的任何方法。但是,可以執行ClassPool的get()方法得到表示同一個類的CtClass實例。執行get()后,ClassPool再次讀取類文件,重新建立CtClass對象。
?
?另一個辦法是用新的ClassPool替換老的。如果老的?ClassPool當做垃圾被回收了,它里面的CtClass對象也會被回收,建立新的ClassPool實例,執行下面的代碼片段:
???????ClassPool?cp?=?new?ClassPool(true); ???????//?if?needed,?append?an?extra?search?path?by?appendClassPath()?通過ClassPool.getDefault()構建默認行為的ClassPool對象。注意ClassPool.getDefault()是為了方便提供的單子工廠方法。它保持著單獨的對象并重用它。getDefault()返回的ClassPool對象沒有特別的作用。getDefault()是一個方便的方法。
?注意new?ClassPool(true)是個方便的構造器,它構建一個ClassPool對象并加入系統搜索路徑。執行它等同于下面的代碼:
ClassPool?cp?=?new?ClassPool(); cp.appendSystemPath();?//?or?append?another?path?by?appendClassPath()?
級聯的ClassPools
如果程序運行在web服務器中,可能需要建立多個ClassPool;要為每個類載入器建一個ClassPool。程序將不用getDefault(),用ClassPool構造器建一個ClassPool對象。
多個CLassPool對象像java.lang.ClassLoader一樣級聯起來。例如,
???????ClassPool?parent?=?ClassPool.getDefault(); ???????ClassPool?child?=?new?ClassPool(parent); ???????child.insertClassPath("./classes");如果執行child.get(),孩子ClassPool首先去查找父ClassPool。如果說父查找失敗,孩子再去./classes目錄下查找類。
如果child.childFirstLookup為true,先在孩子中查找,再到父中查。例如:
??ClassPool?parent?=?ClassPool.getDefault(); ClassPool?child?=?new?ClassPool(parent); child.appendSystemPath();//?the?same?class?path?as?the?default?one. child.childFirstLookup?=?true;???//?changes?the?behavior?of?the?child.改變類名,定義一個新類
一個新類可以被定義為已經存在類的拷貝。看下面的程序:
?????ClassPool?pool?=?ClassPool.getDefault();CtClass?cc?=?pool.get("Point");cc.setName("Pair");這個程序首先得到Point作為CtClass對象,然后用setName()為CtClass設置了新的名字Pair,之后CtClass對象的類名被改為Pari,類定義的其他部分沒有變。??注意?CtClass的setName()改變了ClassPool中的一個記錄。從實現的角度?,setName()改變的是ClassPool?的hash表中的CtClass對象關聯的key。Key從初始的類名改到了新類名。因此,如果get(“Point”)?再次被執行后,將不能返回cc引用的CtClass對象。ClassPool再次讀取Point.class類文件,為Point構建新的CtClass對象。這是因為Point命名的CtClass對象已經不存在了。看下面例子:
?????ClassPool?pool?=?ClassPool.getDefault();CtClass?cc?=?pool.get("Point");CtClass?cc1?=?pool.get("Point");??//?cc1?is?identical?to?cc.cc.setName("Pair");CtClass?cc2?=?pool.get("Pair");???//?cc2?is?identical?to?cc.CtClass?cc3?=?pool.get("Point");??//?cc3?is?not?identical?to?cc.Cc1和cc2引用同一個CtClass實例cc,而cc3不是。注意,cc.setName(“Pair”)執行后,CtClass對象Pair?被cc和cc1引用。
?
ClassPool對象用來維持類和CtClass對象之間一對一的關系映射。Javassist不允許用兩個有區別的CtClass對象表示同一個類,除非建兩個單獨CLassPool。這是對程序轉換前后有意義的功能。
建立另一個ClassPool默認實例的拷貝,用ClassPool.getDefault()得到,執行下面代碼:
????????ClassPool?cp?=?new?ClassPool(true);如果有兩個ClassPool對象,可以分別從ClassPool中得到表示同一個類文件的有區別的CtClass對象。可以編輯不同的CtClass對象用來生成不同的類的版本。??改變凍結類名,定義一個新類當CtClass對象通過writeFile()或toBytecode()轉換為類文件,Javassist將拒絕CtClass對象的修改。因此CtClass表示的Point類是被轉換到類文件中了,執行setName()不能定義Pair類作為Point的拷貝,它將被拒絕。下面的代碼是錯誤的:
ClassPool?pool?=?ClassPool.getDefault();CtClass?cc?=?pool.get("Point");cc.writeFile();cc.setName("Pair");???//?wrong?since?writeFile()?has?been?called.為了避免這個限制,可以執行ClassPool的getAndRename()方法,例如:
ClassPool?pool?=?ClassPool.getDefault();
CtClass?cc?=?pool.get("Point");cc.writeFile();CtClass?cc2?=?pool.getAndRename("Point",?"Pair");?如果執行getAndRename(),ClassPool首先讀取Point.class建立一個新的CtClass。但是,它重命名CtClass從Point改為Pair以前,要先在CtClass的Hash表中作記錄。因此,writeFile()或toBytecode()執行后可以執行getAndRename()。from:?https://blog.csdn.net/qbg19881206/article/details/8992333
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的JavaSist之ClassPool的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ClassPool CtClass浅析
- 下一篇: java美元兑换,(Java实现) 美元