javassist简介
一个比较好的例子:http://yucaifu1989.iteye.com/blog/1850500
比较好的文档:
Javassist是一个开源的java字节码操作工具,主要是对已经编译好的class文件进行修改和处理,这里我写了一个简单的说明,复杂的请去看www.javassist.com的官方文档。
亲测实例
1、首先去官网下载jar http://jboss-javassist.github.io/javassist/ 里面有例子。
2、同样的使用使用jd-gui打开要修改的jar,找到要修改的类名、方法名。
3、在2的基础上开始写修改代码
package start;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class Test1 {
public static void main(String[] args) throws Exception {
//创建类池(就是jar包),用来加载类
ClassPool cp=ClassPool.getDefault();
//jar包所在位置,如果这句代码不写,下面引用的类就是项目中引用的类
cp.insertClassPath("e:/checkLicense-3-3.1.jar");
//获取要修改的具体类,注意是完整路径包括类名
CtClass cc=cp.get("com.primeton.licensemanager.checklicense.LicenseCheckManager");
//获取要修改的具体方法
CtMethod cm=cc.getDeclaredMethod("checkHardware");
//开始对方法进行修改,这里是往方法的最前面插入了一条语句
cm.insertBefore("if(true)return;");
//保存,执行后会在工程下生成一个新的class文件,刷新一下项目即可看到
cc.writeFile();
}
}
4、查看新生成的文件,将生成的新class文件替换jar包以前的class即可使用了。
这块有意思,编译器将true生成了1!=0了.....
在指定位置添加代码
// 在这个方法的182行添加关闭文件流的方法
method.insertAt(182, "fin.close();");
指定生成新class位置
ctClass.writeFile("path");
加入try cath块
addCatch()
addCatch() 指的是在方法中加入try catch 块,需要注意的是,必须在插入的代码中,加入return 值$e代表 异常值。比如:
CtMethod m = ...;
CtClass etype = ClassPool.getDefault().get("java.io.IOException");
m.addCatch("{ System.out.println($e); throw $e; }", etype);
实际代码如下:
try {
the original method body
}
catch (java.io.IOException e) {
System.out.println(e);
throw e;
}
重写方法体
//获取要修改的具体方法
CtMethod cm=cc.getDeclaredMethod("checkHardware");
cm.setBody("System.out.println(\"\");");
cm.setBody();方法可以重新设置方法体的内容。
cm.setName();重新设置方法名字。
使用方法中的参数
Javassist也提供了一些特殊的变量来代表方法参 数:$1,$2,$args...要注意的是,插入的source文本中不能引用方法本地变量的声明,但是可以允许声明一个新的方法本地变量,除非在程序 编译时加入-g选项。
$0代表的是this,$1代表方法参数的第一个参数、$2代表方法参数的第二个参数,以此类推,$N代表是方法参数的第N个。例如:
1. //实际方法
2. void move(int dx, int dy)
3. //javassist
4. CtMethod m = cc.getDeclaredMethod("move");
5. //打印dx,和dy
6. m.insertBefore("{ System.out.println($1); System.out.println($2); }");
7. 注意:如果javassist改变了$1的值,那实际参数值也会改变。
1. $args 指的是方法所有参数的数组,类似Object[],如果参数中含有基本类型,则会转成其包装类型。需要注意的时候,$args[0]对应的是$1,而不是$0,$0!=$args[0],$0=this。
使用类中定义的属性(字段)
$0 是代表this 也就是对象自己,所以直接使用 $0.字段 进行赋值或者使用即可。
例如字段名字叫 status 就可以写成 $0.status=1;
注意:目前可以确定用这种方式对字段进行赋值是没问题的,使用字段的方法还没有测试过。
使用类中定义的方法
与使用字段方法相似,$0.方法名
例如:方法名字叫 get() 就可以写成 $0.get();
修改构造方法
//获取构造方法集合,也可以指定获取指定参数的构造方法
CtClass cc=....;
CtConstructor[] ctc= cc.getConstructors();
//使用某个构造方法,与普通方法操作相同
ctc[0].insertAfter("{return ;}");