最近重新再看<inside jvm>,對java編譯成的位元組碼結構很感興趣,希望找個工具能夠對.class檔案進行的解析和檢視。沒找到,倒發現javaassist可以對位元組碼進行操作和修改。此工具是jboss項目的一部分,jboss實作aop的基礎。呵呵,開眼界了,原來我們可以直接對位元組碼檔案進行修改,哪怕不知道源檔案(跟反編譯完全不同)。一個簡單例子:
import javassist.*;
class hello {
public void say() {
system.out.println("hello");
}
}
public class test {
public static void main(string[] args) throws exception {
classpool cp = classpool.getdefault();
ctclass cc = cp.get("hello");
ctmethod m = cc.getdeclaredmethod("say");
m.setbody("{system.out.println(/"shit/");}");
m.insertbefore("system.out.println(/"fuck/");");
class c = cc.toclass();
hello h = (hello)c.newinstance();
h.say();
編譯運作此檔案,輸出:
fuck
shit
我們在
ctmethod m = cc.getdeclaredmethod("say");
m.setbody("{system.out.println(/"shit/");}");
m.insertbefore("system.out.println(/"fuck/");");
修改了say()方法,改成了
system.out.println("fuck");
system.out.println("shit");
這裡的classpool是ctclass的容器,它讀取class檔案,并根據要求儲存ctclass的結構以便日後使用,預設狀态下是從目前的類裝載器獲得,當然你可以指定:
pool.insertclasspath("/usr/local/javalib");
當然,不僅僅是修改方法,你還可以建立一個class,利用makeclass()方法,如:
classpool pool = classpool.getdefault();
ctclass cc = pool.makeclass("point");
還可以新增方法,下面是sample裡的一個例子,同樣的:
package sample;
import java.lang.reflect.*;
/*
a very simple sample program
this program overwrites sample/test.class (the class file of this
class itself) for adding a method g(). if the method g() is not
defined in class test, then this program adds a copy of
f() to the class test with name g(). otherwise, this program does
not modify sample/test.class at all.
to see the modified class definition, execute:
% javap sample.test
after running this program.
*/
public int f(int i) {
i++;
return i;
classpool pool = classpool.getdefault();
ctclass cc = pool.get("sample.test");
test test=new test();
class c=test.getclass();
method []method=c.getdeclaredmethods();
for(int i=0;i<method.length;i++){
system.out.println(method[i]);
}
try {
cc.getdeclaredmethod("g");
system.out.println("g() is already defined in sample.test.");
catch (notfoundexception e) {
/* getdeclaredmethod() throws an exception if g()
* is not defined in sample.test.
*/
ctmethod fmethod = cc.getdeclaredmethod("f");
ctmethod gmethod = ctnewmethod.copy(fmethod, "g", cc, null);
cc.addmethod(gmethod);
cc.writefile(); // update the class file
system.out.println("g() was added.");
第一次運作時,因為test裡并沒有g()方法,是以執行
ctmethod fmethod = cc.getdeclaredmethod("f");
ctmethod gmethod = ctnewmethod.copy(fmethod, "g", cc, null); //把f方法複制給g
cc.writefile(); //更新class檔案
列印:g() was added
第2次運作時,因為以上步驟已經在class檔案中增加了一個g方法,是以
system.out.println("g() is already defined in sample.test.");
列印:g() is already defined in sample.test
javassist不僅能修改你自己的class檔案,而且可以同樣修改jdk自帶的類庫(廢話,類庫也是人寫的^_^)具體請看它的tutorial
文章轉自莊周夢蝶 ,原文釋出時間5.17