Javaassist简介
1、簡介
Javassist(JAVA programming ASSISTant)是在Java中編輯字節碼的類庫;它使Java程序能夠在運行時定義一個新類,并在JVM加載是修改類文件。
我們常用到的動態特性主要是反射,在運行時查找對象屬性、方法,修改作用域,通過方法名稱調用方法等。在線的應用不會頻繁使用反射,因為反射的性能開銷較大。其實還有一種和反射一樣強大的特性,但是開銷卻很低,他就是Javassist。
與其他類似的字節碼編輯器不同,Javassist提供了兩個級別的API:源級別和字節碼級別。如果用戶使用源級API,他們可以編輯類文件,而不知道Java字節碼的規格。整個API只用Java語言的詞匯來設計。您甚至可以以源文本的形式指定插入的字節碼;Javassist在運行中編譯它。另一方面,字節碼級API允許用戶直接編輯類文件作為其他編輯器。
2、讀取和寫入字節碼
類Javassist.CtClass是類文件的抽象表示形式。CtClass(編譯時類)對象是處理類文件的句柄。下面的程序是一個非常簡單的示例:
ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("test.Rectangle"); cc.setSuperclass(pool.get("test.Point")); cc.writeFile();該程序首先獲得一個ClassPool對象,它通過Javassist對象字節碼修改。ClassPool對象是表示類文件的CtClass對象的容器。它根據需要讀取類文件以構造CtClass對象,并記錄構造對象以響應以后的訪問。若要修改類的定義,用戶必須首先從ClassPool對象獲取對表示該類的CtClass對象的引用。ClassPool對象獲得的,它被分配給一個變量cc。getDefault返回的ClassPool對象搜索默認的系統搜索路徑。
可以修改從ClassPool對象獲得CtClass對象(稍后將介紹如何修改CtClass的詳細信息)。在上面的例子中,它被修改以便測試的超類。將矩形更改為類測試點。當最終調用CtClass()中的writeFile()時,此更改將反映在原始類文件中。
writeFile()時,此更改將反映在原始類文件中。
writeFile()將CtClass對象轉換為類文件,并將其寫入本地磁盤。Javassist還提供了一種直接獲取修改后的字節碼的方法。要獲取字節碼,請調用toBytecode():
byte[] b = cc.toBytecode();您還可以直接加載CtClass:
Class clazz = cc.toClass();toClass()請求當前線程的上下文類加載程序加載由CtClass表示的類文件。它返回一個表示已加載類的java.lang.Class對象。
2.1、定義類
ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeCLass("Point");此程序定義一個類Point,包括沒有成員。可以使用CtNewMethod中聲明的工廠方法創建點的成員方法,并在CtClass中追加到點與addMethod()。
makeClass()無法創新接口;可以使用 makeInterface () 做。接口中的成員方法可以在 CtNewMethod 中使用 abstractMethod () 創建。請注意, 接口方法是一種抽象方法。
2.2、凍結類
如果CtClass對象由writeFile()、toClass()或toBytecode()轉換為類文件,Javassist將凍結該CtClass對象。那CtClass對象的進一步修改不被允許。這是為了在開發人員試圖修改已加載的類文件時發出警告,因為JVM不允許重新加載類。
凍結的CtClass可以解凍,一遍允許對類定義進行修改。例如,
CtClass cc = ...;cc.writeFile(); cc.defrost(); cc.setSuperclass(...); // OK since the class is not frozen.2.3、類搜索路徑
靜態方法ClassPool.getDefault()返回的默認ClassPool將搜索底層JVM(Java虛擬機)具有的同一路徑。如果某個程序在web應用程序服務器(如JBoss和Tomcat)上運行,則ClassPool對象可能無法找到用戶類,因為這樣的web應用程序服務器使用多個類加載器以及系統類加載程序。在這種情況下,必須將附加的類路徑注冊到ClassPool。假設池引用的是ClassPool對象:
pool.insertClassPath(new ClassClassPath(this.getClass()));此語句注冊用于加載次飲用的對象的類的類路徑。可以將任何類對象用作參數而不是this.getClass()。用于加載由該類對象表示的類的類路徑已注冊。可以將目錄名注冊為類搜索路徑。例如,下面的代碼將目錄/usr/local/javalib添加到搜索路徑中:
ClassPool pool = ClassPool.getDefault(); pool.insertClassPath("/usr/local/javalib");用戶可以添加的搜索路徑不僅是一個目錄,而且可以是一個URL:
ClassPool pool = ClassPool.getDefault(); ClassPath cp - new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist."); pool.insertClassPath(cp);此程序將"http://www.javassist.org:80/java/"添加到類搜索路徑中。此URL僅用于搜索屬于包組織javassist的類。例如,要加載類org.javassist.test.Main,將從以下內容獲取其類文件:
http://www.javassist.org:80/java/org/javassist/test/Main.class此外,您可以直接給ClassPool對象一個字節數組,并從該數組構造一個CtClass對象。為此,請使用ByteArrayClassPath.例如:
ClassPool cp = ClassPool.getDefault(); byte[] b = a byte array; String name = class name; cp.insertClassPath(new ByteArrayClassPath(name, b)); CtClass cc = cp.get(name);獲得的CtClass對象表示由b指定的類文件定義的類。ClassPool從給定的ByteArrayClassPath讀取類文件(如果調用了get(),并且給定的類名為get()等于名稱指定的類別。
如果您不知道該類的完全限定名,則可以在ClassPool中使用makeClass():
ClassPool cp = ClassPool.getDefault(); InputStream ins = an input stream for reading a class file; CtClass cc = cp.makeClass(ins);makeClass()從給定輸入流返回構造的CtClass對象。您可以使用makeClass()將類文件送到ClassPool對象。如果搜索路徑包含大jar文件,這可能會提高性能。由于ClassPool對象根據需要讀取類文件,因此它可能會反復搜索每個類文件的整個jar文件。makeClass()可用于優化此搜索。由makeClass()構造的CtClass保存在ClassPool對象中,不再讀取類文件。
3、小結
本文簡要介紹了javaassist及其簡單用法。會有一些讀者好奇:它和AOP有什么關系和區別?舉個簡單的例子即可:CGLib是動態代理的經典類庫,其底層實現使用ASM,javaassist是類似ASM的東東。
4、參考文獻
?
總結
以上是生活随笔為你收集整理的Javaassist简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql_connect报告“No s
- 下一篇: JVM插桩之二:Java agent基础