javassist入門

javassist的基本功能

Javassist是一個動態類庫,可以用來檢查、”動態”修改以及創建 Java類。其功能與jdk自帶的反射功能類似,但比反射功能更強大。

重要的類

ClassPool:javassist的類池,使用ClassPool 類可以跟蹤和控制所操作的類,它的工作方式與 JVM 類裝載器非常相似,
CtClass: CtClass提供了檢查類數據(如字段和方法)以及在類中添加新字段、方法和構造函數、以及改變類、父類和接口的方法。不過,Javassist 並未提供刪除類中字段、方法或者構造函數的任何方法。
CtField:用來訪問域
CtMethod :用來訪問方法
CtConstructor:用來訪問構造器

語法

使用javassist來編寫的代碼與java代碼不完全一致,主要的區別在於 javassist提供了一些特殊的標記符(以System.out.println(Argument1:+ 1); 其中的$1表示第1個參數.

示例

可以通過javassist來修改java類的方法,來修改其實現。如下所示:

      ClassPool classPool = ClassPool.getDefault();
      CtClass ctClass = classPool.get("org.light.lab.JavassistTest");
      CtMethod ctMethod = ctClass.getDeclaredMethod("test");
      ctMethod.setBody("System.out.println(\"this method is changed dynamically!\");");
      ctClass.toClass();

上面的方法即是修改一個方法的實現,當調用ctClass.toClass()時,修改後的類將被當前的ClassLoader加載並實例化。

完整的代碼:

public class ChangeDemo {

  public static void main(String[] args) throws NotFoundException, CannotCompileException {
       replaceMethodBody("foo.Student", "execute", "System.out.println(\"this method is changed dynamically!\");");
       Student student = new Student();
       student.execute();
   }

public static void replaceMethodBody(String clazzName, String methodName, String newMethodBody) {
    try {
        CtClass clazz = ClassPool.getDefault().get(clazzName);
        CtMethod method = clazz.getDeclaredMethod(methodName);
        method.setBody(newMethodBody);
        clazz.toClass();
    } catch (NotFoundException | CannotCompileException e) {
       throw new RuntimeException(e);
    }
}

}

限制與侷限性

  • 需要注意的是,在調用ctClass.toClass()時,會加載此類,如果此類在之前已經被加載過,則會報一個duplicate load的錯誤,表示不能重複加載一個類。所以,修改方法的實現必須在修改的類加載之前進行。
  • 不能訪問塊之外的局部變量。如果在一個方法的開始和結尾都增加了代碼段,那麼在方法的結尾塊中無法訪問方法開始中的代碼段中的變量(不太完美的解決方法是將原方法改名,然後再增加與原方法同名的方法)。

與aspectj的區別

使用aspectj也可以同樣達到修改的效果,不過修改指定的類,則需要爲修改這個類添加一個aspect,然後將這個aspect加入配置文件中以使其生效,比起javassist來說,修改一個類還是使用javassist相對簡單一點。

容器中的Classpath

在tomcat之類的容器中是無法通過ClassPool.getDefault()獲取到用戶定義的類的,可以通過以下方法獲取:
pool.insertClassPath(new ClassClassPath(this.getClass()));
或者:
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(“/usr/local/javalib”);

參考資料:

http://www.ibm.com/developerworks/cn/java/j-dyn0916/
http://jboss-javassist.github.io/javassist/tutorial/tutorial.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章