用Javassit完了一段时间,感觉挺牛逼的,可以轻松的操作java的字节码文件,这个东西相当于ASM是轻松不少了,对了记得获取已存在类对象的时候,通过类名称
com.yellowcong.test.User
来获取,而不是通过类对象来获取,因为通过类对象来获取,就会导致加载了两个不同版本的类在JVM中,就会报错,当CtClass 调用writeFile()、toClass()、toBytecode() 这些方法的时候,Javassist会冻结CtClass Object,对CtClass object的修改将不允许。
创建类对象
可以通过javassit来动态创建类文件,然后使用类文件对象,动态添加字段,set,get方法,以及添加自定义方法,
/**
* 通过javassist 来自己创建类对象
*
* @throws Exception
*/
public static void testCreateObject() throws Exception {
ClassPool pool = ClassPool.getDefault();
// 创建类
CtClass ctclazz = pool.makeClass("com.yellowcong.test.User");
// 创建属性 String 类型的数据不是基本数据类型,所以不能通过CtClass获取
CtField usernameField = new CtField(pool.get(String.class.getName()), "username", ctclazz);
// 添加set和get方法
ctclazz.addMethod(CtNewMethod.setter("setUsername", usernameField));
ctclazz.addMethod(CtNewMethod.getter("getUsername", usernameField));
ctclazz.addField(usernameField);
// 添加int类型的数据
CtField ageField = new CtField(CtClass.intType, "age", ctclazz);
// 添加set和get方法
ctclazz.addMethod(CtNewMethod.setter("setAge", ageField));
ctclazz.addMethod(CtNewMethod.getter("getAge", ageField));
ctclazz.addField(ageField);
// 添加构造器,不带参数的
CtConstructor con = new CtConstructor(null, ctclazz);
con.setModifiers(Modifier.PUBLIC);
con.setBody("{System.out.println('init constructor');}");
ctclazz.addConstructor(con);
// 添加构造器,带参数的
CtConstructor con2 = new CtConstructor(new CtClass[] { pool.get(String.class.getName()), CtClass.intType },
ctclazz);
con2.setModifiers(Modifier.PUBLIC);
con2.setBody("{this.username = $1;this.age = $2;}");
ctclazz.addConstructor(con2);
// 添加方法
CtMethod say = new CtMethod(CtClass.voidType, "say", null, ctclazz);
say.setModifiers(Modifier.PUBLIC);
say.setBody("{System.out.print(\"我是\"+this.username+\",今年\"+this.age+\"岁\");}");
ctclazz.addMethod(say);
Class<?> clz = ctclazz.toClass();
// 获取构造器,实例化对象
Object obj = clz.getConstructor(String.class, int.class).newInstance("yellowcong", 18);
// 获取动态添加的方法
clz.getMethod("say", null).invoke(obj, null);
// 设定用户名称
clz.getMethod("setUsername", String.class).invoke(obj, "doubi");
Object ret = clz.getMethod("getUsername", null).invoke(obj, null);
System.out.println(ret.toString());
}
更改以存在对象的类方法
在已有类的基础上,添加方法
/**
* 更改类结构已经存在的方法
* 在已有类的基础上,添加aop类似的操作,但是没有用到javassist的代理操作
* @throws Exception
*/
public static void changeClsMethod() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cls = pool.get("com.yellowcong.test.TestDemo");
// 获取到所有的字段
CtField[] fields = cls.getFields();
for (CtField field : fields) {
System.out.println(field.getName());
}
// 对于String类型,CtClass中,没有这个数据类型,只有基本的8中数据类型
CtMethod method = cls.getDeclaredMethod("say", new CtClass[] { pool.get("java.lang.String") });
// 复制方法
CtMethod newMethod = CtNewMethod.copy(method, cls, null);
// 修改原来方法的名称
String methodName = method.getName() + "$Impl";
method.setName(methodName);
// 这个 inertBefore方法没有问题
// method.insertBefore("System.out.println($1);say($$);System.out.println(\"start!!!\");");
// insertAfter方法有问题
// method.insertAfter("System.out.println(\"end!!!\");");
// 修改方法体
// method.setBody("System.out.println(\"你没机会了\");");
// 修改原方法
newMethod.setBody("{System.out.println(\"执行前\");" + methodName + "($$);System.out.println(\"执行后\");}");
cls.addMethod(newMethod);
Class<?> me = cls.toClass();
Object obj = me.newInstance();
me.getDeclaredMethod("say", String.class).invoke(obj, "test");
}
修改类已存在的方法2
直接不要类以前的方法,然后直接覆盖,设定自己的方法体
/**
* 修改类里面的方法
*/
public static void changeMethodBody() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClazz = pool.get("com.yellowcong.test.TestDemo");
// 获取TestDemo里面的public void say(String name) 方法
CtMethod method = ctClazz.getDeclaredMethod("say", new CtClass[] { pool.get(String.class.getName()) });
//修改方法内容,这个方法已经存在,不用在添加了
method.setBody("{System.out.print(\"你是逗比\t\"+$1);}");
method.insertBefore("System.out.println(\"end!!!\");");
//对于inserAfter使用不了,具体是啥原因还不清楚
Class<?> clazz = ctClazz.toClass();
Object obj = clazz.newInstance();
clazz.getMethod("say", String.class).invoke(obj, "yellowcong");
}
完整的代码
package com.yellowcong.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
/**
*
* 作者:yellowcong 日期:2017/09/01 時間:12:07:33 描述:
*/
public class Demo10 {
public static void main(String[] args) throws Exception, NotFoundException {
changeMethodBody() ;
}
/**
* 修改类里面的方法
*/
public static void changeMethodBody() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClazz = pool.get("com.yellowcong.test.TestDemo");
// 获取TestDemo里面的public void say(String name) 方法
CtMethod method = ctClazz.getDeclaredMethod("say", new CtClass[] { pool.get(String.class.getName()) });
//修改方法内容,这个方法已经存在,不用在添加了
method.setBody("{System.out.print(\"你是逗比\t\"+$1);}");
method.insertBefore("System.out.println(\"end!!!\");");
//对于inserAfter使用不了,具体是啥原因还不清楚
Class<?> clazz = ctClazz.toClass();
Object obj = clazz.newInstance();
clazz.getMethod("say", String.class).invoke(obj, "yellowcong");
}
/**
* 更改类结构已经存在的方法
* 在已有类的基础上,添加aop类似的操作,但是没有用到javassist的代理操作
* @throws Exception
*/
public static void changeClsMethod() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cls = pool.get("com.yellowcong.test.TestDemo");
// 获取到所有的字段
CtField[] fields = cls.getFields();
for (CtField field : fields) {
System.out.println(field.getName());
}
// 对于String类型,CtClass中,没有这个数据类型,只有基本的8中数据类型
CtMethod method = cls.getDeclaredMethod("say", new CtClass[] { pool.get("java.lang.String") });
// 复制方法
CtMethod newMethod = CtNewMethod.copy(method, cls, null);
// 修改原来方法的名称
String methodName = method.getName() + "$Impl";
method.setName(methodName);
// 这个 inertBefore方法没有问题
// method.insertBefore("System.out.println($1);say($$);System.out.println(\"start!!!\");");
// insertAfter方法有问题
// method.insertAfter("System.out.println(\"end!!!\");");
// 修改方法体
// method.setBody("System.out.println(\"你没机会了\");");
// 修改原方法
newMethod.setBody("{System.out.println(\"执行前\");" + methodName + "($$);System.out.println(\"执行后\");}");
cls.addMethod(newMethod);
Class<?> me = cls.toClass();
Object obj = me.newInstance();
me.getDeclaredMethod("say", String.class).invoke(obj, "test");
}
/**
* 通过javassist 来自己创建类对象
*
* @throws Exception
*/
public static void testCreateObject() throws Exception {
ClassPool pool = ClassPool.getDefault();
// 创建类
CtClass ctclazz = pool.makeClass("com.yellowcong.test.User");
// 创建属性 String 类型的数据不是基本数据类型,所以不能通过CtClass获取
CtField usernameField = new CtField(pool.get(String.class.getName()), "username", ctclazz);
// 添加set和get方法
ctclazz.addMethod(CtNewMethod.setter("setUsername", usernameField));
ctclazz.addMethod(CtNewMethod.getter("getUsername", usernameField));
ctclazz.addField(usernameField);
// 添加int类型的数据
CtField ageField = new CtField(CtClass.intType, "age", ctclazz);
// 添加set和get方法
ctclazz.addMethod(CtNewMethod.setter("setAge", ageField));
ctclazz.addMethod(CtNewMethod.getter("getAge", ageField));
ctclazz.addField(ageField);
// 添加构造器,不带参数的
CtConstructor con = new CtConstructor(null, ctclazz);
con.setModifiers(Modifier.PUBLIC);
con.setBody("{System.out.println('init constructor');}");
ctclazz.addConstructor(con);
// 添加构造器,带参数的
CtConstructor con2 = new CtConstructor(new CtClass[] { pool.get(String.class.getName()), CtClass.intType },
ctclazz);
con2.setModifiers(Modifier.PUBLIC);
con2.setBody("{this.username = $1;this.age = $2;}");
ctclazz.addConstructor(con2);
// 添加方法
CtMethod say = new CtMethod(CtClass.voidType, "say", null, ctclazz);
say.setModifiers(Modifier.PUBLIC);
say.setBody("{System.out.print(\"我是\"+this.username+\",今年\"+this.age+\"岁\");}");
ctclazz.addMethod(say);
Class<?> clz = ctclazz.toClass();
// 获取构造器,实例化对象
Object obj = clz.getConstructor(String.class, int.class).newInstance("yellowcong", 18);
// 获取动态添加的方法
clz.getMethod("say", null).invoke(obj, null);
// 设定用户名称
clz.getMethod("setUsername", String.class).invoke(obj, "doubi");
Object ret = clz.getMethod("getUsername", null).invoke(obj, null);
System.out.println(ret.toString());
}
}