Java之Javassist对类的常用操作小结-yellowcong

xiaoxiao2021-02-28  101

用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()); } }
转载请注明原文地址: https://www.6miu.com/read-84213.html

最新回复(0)