一,概述
Javassist是一個動態位元組碼產生架構,包括支援兩種API,Java語言層級和位元組碼層級。其中有些特性是受限的或者不支援的,尤其是JDK1.5裡的部分新特性。
最經常使用的是來提升POJO的能力。
具體技術內容請參考官方文檔
http://www.csg.is.titech.ac.jp/~chiba/javassist/
二,ClassPool
ClassPool 緩衝CtClass的池。體系有些類似java中的ClassLoader,有自己的命名空間。
JVM不允許動態載入類,因此Javassist也提供frozen能力;當然javassist還提供解凍能力。
Javassist還提供修剪pruning的能力,以節省記憶體。如果被修剪,那麼一個類的方法元資訊將不能再被訪問,除了方法名,方法簽名和annotations。一個被prune的類,經過writeFile()或者toBytecode()是不能再被解凍的。因為其元資訊丟失。
在有些情景下,開發人員需要組織自己的ClassPool體繫結構。如果在Tomcat等存在多重ClassLoader的環境中。
ClassPool支援多種添加類路徑的方式,如URL,Inputtream,byte[]等。
其最經典的使用方式
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Rectangle");
cc.setSuperclass(pool.get("test.Point"));
cc.writeFile();
CtClass可以從ClasPool中脫離,以節省記憶體,通過
cc.detach();
建立ClassPool的方式
ClassPool pool = ClassPool.getDefault(); //便捷方式,維護一個單例
等同於
ClassPool cp = new ClassPool(true);
等同於
ClassPool cp = new ClassPool();
cp.appendSystemPath();
在ClassPool載入類的體系中可以指定Child優先,通過如下代碼:
child.childFirstLookup = true;
Javassist可以更改類名(未凍結)
CtClass.setName("newName");
或者更改已被凍結的類以建立新類
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
CtClass cc2 = pool.getAndRename("Point", "Pair");
三,使用ClassLoader
在要修改的類不能事先確定的情況下,不能使用上面的方式更改類,必須配合ClassLoader。當然Javassist也提供了ClassLoader的擴充。
CtClass.toClass();
需要提及的是要選對ClassLoader。例如使用bean對象的載入器
Class c = cc.toClass(bean.getClass().getClassLoader());
載入類時修改
使用javassist.Loader和其事件監聽器機制
修改系統類別
運行時動態重載入類(reload)
4. 內醒和定製Introspection and customization
Javassist 不能刪除方法或者域成員;不能給方法添加參數,必須通過建立新的方法滿足。
Javassist通過內聯的方式給方法添加代碼塊。方法的局部變數名是無法得到的,除非用-g參數編譯類。通過$做首碼來訪問。
insertBefore(), insertAfter(), andaddCatch()
Javassist提供了各種類來協助修改不同類型的資料,其中$在不同的類型中意義有所差別或者Not available.
javassist.expr.MethodCall
javassist.expr.ConstructorCall
javassist.expr.FieldAccess
javassist.expr.NewExpr
javassist.expr.NewArray
javassist.expr.Instanceof
javassist.expr.Cast
javassist.expr.Handler//處理異常
4.3 Adding a new method or field
添加方法時,如果方法之間有依賴調用,如果找不到定義則會失敗。因此需要通過添加抽象方法的方式實現
CtClass cc = ... ;
CtMethod m = CtNewMethod.make("public abstract int m(int i);", cc);
CtMethod n = CtNewMethod.make("public abstract int n(int i);", cc);
cc.addMethod(m);
cc.addMethod(n);
m.setBody("{ return ($1 <= 0) ? 1 : (n($1 - 1) * $1); }");
n.setBody("{ return m($1); }");
cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
添加域,應該賦初始值。
注意:Javassist產生的類一般不需要特殊輔助即可運行,但有些特殊情況需要用到一下包
javassist.runtime
4.7限制
enums and generics have not been supported.使用範型時,以普通類型對待。
5. Bytecode level API
位元組碼層級的API提供了更強的能力,包括添加和刪除成員(包括方法)。
6.範型
7. Varargs可變參數
Javassist不支援可變參數,將轉成數組來訪問。