Javassist 讀寫字節碼

原文地址:奇舞移動技術java

Javassist是一個用於處理Java字節碼的庫。Java字節碼以二進制的形式存儲在class文件中。 每一個class文件包含一個Java類或接口。編程

class文件能夠用Javassist.CtClass類來表示。CtClass對象用於處理class文件。如下是一個簡單的例子,有兩個類,這兩個類沒有關係,咱們修改Rectangle使他的父類編程Point。數組

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.democome.Rectangle");
cc.setSuperclass(pool.get("com.democome.Point"));
cc.writeFile();
複製代碼

Rectangle.java服務器

public class Rectangle {
	private int width;
	private int height;
}
複製代碼

Point.java微信

public class Point {

}
複製代碼

運行以後,用反編譯工具能夠看一下,繼承關係已經改變:數據結構

1

上面的代碼首先得到一個ClassPool。ClassPool是CtClass的容器。它讀取class文件來構造CtClass對象,並記錄。要修改calss,首先要從ClassPool經過get()獲取一個CtClass對象。工具

關於實現的原理ClassPool中有一個Hashtable來存儲CtClass,key就是類名。ClassPool的get()方法若是能找到這個類則直接返回,不然建立一個CtClass對象返回,並存到Hashtable中。this

private Hashtable cflow = null;spa

CtClass對象對class文件進行修改,並調用writeFile()寫入文件。Javassist還提供了一種直接獲取修改後的字節碼的方法:3d

byte[] b = cc.toBytecode();
複製代碼

也能夠直接加載class:

Class clazz = cc.toClass();
複製代碼

定義一個類

定義一個類,須要調用ClassPool的makeClass()方法。

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
複製代碼

以上代碼定義了一個類Point。能夠用CtNewMethod建立一個方法,並使用CtClass中的addMethod()方法添加到Point。

cc.addMethod(CtNewMethod.make("public void hello(){System.out.print(\"hello\");}", cc));
cc.writeFile();
複製代碼

2

若是要建立新接口,能夠用ClassPool中的makeInterface()方法。能夠用CtNewMethod中的abstractMethod()建立接口中的成員方法。

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeInterface("Point");
cc.addMethod(CtNewMethod.abstractMethod(CtClass.voidType, "hello", null, null, cc));
cc.writeFile();
複製代碼

3

凍結類

若是經過writeFile(),toClass()或toBytecode()將CtClass對象轉換爲類文件,Javassist將凍結該CtClass對象。 不容許對該CtClass對象進行進一步修改。 這是爲了在開發人員嘗試修改已加載的類文件時警告開發人員,由於JVM不容許從新加載類。

凍結的CtClass能夠解凍,以便容許修改類定義。 例如,

解凍以後能夠修改,以下:

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.democome.Rectangle");
cc.writeFile();
cc.defrost();
cc.setSuperclass(pool.get("com.democome.Point"));
複製代碼

若是ClassPool.doPruning設置爲true,那麼當Javassist凍結該對象時,Javassist會修剪CtClass對象中包含的數據結構。修剪過的CtClass對象沒法再次解凍。ClassPool.doPruning的默認值爲false。要禁止修剪特定的CtClass,必須事先在該對象上調用stopPruning():

類搜索路徑

ClassPool.getDefault()返回的ClassPool,類搜索路徑和JVM相同。若是程序在諸如JBoss和Tomcat之類的Web應用程序服務器上運行,那麼ClassPool可能沒法找到用戶類,由於這樣的Web應用程序服務器使用多個類加載器以及系統類加載器。在這種狀況下,必須在ClassPool中註冊其餘類路徑。

pool.insertClassPath(new ClassClassPath(this.getClass()));
複製代碼

以上代碼註冊用於加載此引用的對象的類的類路徑。可使用任何Class對象做爲參數而不是this.getClass()。用於加載由該Class表示的類的類路徑。

還能夠將目錄註冊爲類搜索路徑。 例如,如下代碼將目錄/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);
複製代碼

以上代碼將「www.javassist.org:80/java/」添加到類搜…

http://www.javassist.org:80/java/org/javassist/test/Main.class
複製代碼

還可使用ByteArrayClassPath直接向ClassPool對象提供一個字節數組,並從該數組構造一個CtClass對象。例如:

ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.democome.Rectangle");
byte[] b = cc.toBytecode();
String name = "Rectangle";
cp.insertClassPath(new ByteArrayClassPath(name, b));
CtClass cc2 = cp.get(name);
複製代碼

若是不知道該類的徹底限定名,則能夠在ClassPool中使用makeClass():

ClassPool cp = ClassPool.getDefault();
InputStream ins = an input stream for reading a class file;
CtClass cc = cp.makeClass(ins);
複製代碼

例如:

ClassPool cp = ClassPool.getDefault();
InputStream ins = new FileInputStream(
		"/Users/yangpeng/Documents/workspace/javassist/Javassist/com/democome/Rectangle.class");
CtClass cc = cp.makeClass(ins);
System.out.println(cc.getName());

複製代碼

打印結果以下:

com.democome.Rectangle
複製代碼

關注微信公衆號,最新技術乾貨實時推送

image
相關文章
相關標籤/搜索