天天看點

Eclipse下Javassist正确使用方法

Eclipse下Javassist正确使用方法

        這兩天看到Hibernate的代理部分,第一反應是底層使用了反射,針對使用者實體生成了代理類,後來反應過來了,反射沒有任何可以産生新類的能力,也就順理成章地找到了Javassist。

        在網上搜尋到的大部分教程,都是針對Javassist的API進行一番講解,但是最後,往往沒有一個加載過程,而筆者模仿這些教程進行類的加載時,加載到的結果都是原來的類,并沒有産生位元組碼被修改的内容。

        在經過一番探索後,筆者發現,網上的大部分教程中的最後一步,儲存位元組碼,使用的均是writeFile的無參數重載,在檢視其函數結構後發現,它還有一個String類型的重載,由于在Eclipse下,位元組碼儲存的根位置并不是”.\\”而是”.\\bin”,而writeFile的另一個重載很可能是指定位元組碼根位置的參數,筆者進行一番更改後發現,果不其然。

下面将示範代碼分享出來:

這是筆者項目的結構:

Eclipse下Javassist正确使用方法
Editable.java:
package com.thrblock.javassist;
 
public class Editable {
      public void showInfo(){
             System.out.println("InfoDefault!");
      }
}
           
Main.java:
package com.thrblock.javassist;
 
import java.io.IOException;
 
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
 
public class Main {
      public static void main(String[] args) {
             ClassPool pool = ClassPool.getDefault();
             try{
                    pool.insertClassPath(".\\bin");//設定根路徑。(這裡設定的根路徑顯然沒被writeFile使用)
 
                    CtClass cc = pool.makeClass("com.thrblock.javassist.EditableChanged");//模拟Hibernate代理模式,我們建立一個新類
                    cc.setSuperclass(pool.get("com.thrblock.javassist.Editable"));//設定其父類
                    CtMethodcm = CtNewMethod.make("public void showInfo(){super.showInfo();System.out.println(\"CustomInsertHAHA!\");}",cc);//追加一個方法,注意它覆寫了父類中的方法。
                    cc.addMethod(cm);
 
                    cc.writeFile(".\\bin");//這裡比較重要,空參的結果就是沒有儲存到eclipse位元組碼根路徑裡。
             }catch (NotFoundException | CannotCompileException | IOException e) {
                    e.printStackTrace();
             }
             try{
                    Class<?> cl = Class.forName("com.thrblock.javassist.EditableChanged");//加載我們的新類
                    Editableed = (Editable) cl.newInstance();//由于其繼承與Editable類,這裡和Hibernate裡的load道理一樣。
                    ed.showInfo();//調用方法。
             }catch (ClassNotFoundException | InstantiationException |IllegalAccessException e) {
                    e.printStackTrace();
             }
      }
}
           

列印結果:

Info Default!

CustomInsert HAHA!

其他注意事項:

由于我們是生成了一個類,如果這個類名和原類名一樣,則會覆寫class檔案,但是如果修改之前該class已經被JVM裝入,則修改的部分不會生效,必須重新開機JVM。