最 近重新再看<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(/"/");}");
m.insertBefore("System.out.println(/"/");");
Class c = cc.toClass();
Hello h = (Hello)c.newInstance();
h.say();
}
}
编译运行此文件,输出:
我们在
CtMethod m = cc.getDeclaredMethod("say");
m.setBody("{System.out.println(/"/");}");
m.insertBefore("System.out.println(/"/");");
修改了say()方法,改成了
System.out.println("");
System.out.println("");
这里的ClassPool是CtClass的容器,它读取class文件,并根据要求保存CtClass的结构以便日后使用,默认状态下是从当前的类装载器获得,当然你可以指定:
pool.insertClassPath("/usr/local/javalib");
当然,不仅仅是修改方法,你还可以新建一个class,利用makeClass()方法,如:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
还可以新增方法,下面是sample里的一个例子,同样的:
package sample;
import javassist .*;
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 class Test {
public int f(int i) {
i++;
return i;
}
public static void main(String[] args) throws Exception {
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.addMethod(gMethod);
cc.writeFile(); //更新class文件
System.out.println("g() was added.");
打印:g() was added
第2次运行时,因为以上步骤已经在class文件中增加了一个g方法,所以
System.out.println("g() is already defined in sample.Test.");
打印:g() is already defined in sample.Test