使用javassist生成新类
来源:互联网 发布:sql 查询分析器 编辑:程序博客网 时间:2024/06/05 01:35
javassist
javassist是一个开源的分析、编辑和创建Java字节码的类库。不需要了解虚拟机指令,就能动态生成类或者改变类的结构。
ClassPool
ClassPool是缓存CtClass对象的容器,所有的CtClass对象都在ClassPool中。所以,CtClass对象很多时,ClassPool会消耗很大的内存,为了避免内存的消耗,创建ClassPool对象时可以使用单例模式,或者对于CtClass对象,调用detach方法将其从ClassPool中移除。
创建ClassPool对象
构造函数1
public ClassPool()
创建一个根ClassPool对象
构造函数2
public ClassPool(boolean useDefaultPath)
创建一个根ClassPool对象,当参数为true时,appendSystemPath将被调用。
构造函数3
public ClassPool(ClassPool parent)
创建一个指定根的ClassPool对象,若无根,则参数为null。
单例
public static ClassPool getDefault()
创建默认的ClassPool对象,该方法是单例的。当调用该方法时,等同于下面代码的调用。
ClassPool cp = new ClassPool(); cp.appendSystemPath();
修改生成的类
首先生成一个类
public class ClassGeneratedByJavassist { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.study.javassist.MyCC"); // 添加一个参数 CtField ctField = new CtField(CtClass.intType, "id", ctClass); ctField.setModifiers(Modifier.PUBLIC); ctClass.addField(ctField); // 把生成的class文件写入文件 byte[] byteArr = ctClass.toBytecode(); FileOutputStream fos = new FileOutputStream(new File("D://MyCC.class")); fos.write(byteArr); fos.close(); System.out.println("over!!"); } }
通过XJad反编译,结果如下:
package com.study.javassist; public class MyCC { public int id; public MyCC() { } }
下面对MyClass.Java再添加一个name属性,如下:
public class ClassGeneratedByJavassist { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.study.javassist.MyCC"); // 添加一个参数 CtField ctField = new CtField(CtClass.intType, "id", ctClass); ctField.setModifiers(Modifier.PUBLIC); ctClass.addField(ctField); // 把生成的class文件写入文件 byte[] byteArr = ctClass.toBytecode(); FileOutputStream fos = new FileOutputStream(new File("D://MyCC.class")); fos.write(byteArr); fos.close(); System.out.println("over!!"); // 为了测试ctClass是否能够再修改,再添加一个域 CtField ctField2 = new CtField(pool.get("java.lang.String"), "name", ctClass); ctField2.setModifiers(Modifier.PUBLIC); ctClass.addField(ctField2); byteArr = ctClass.toBytecode(); fos = new FileOutputStream(new File("D://MyCC.class")); fos.write(byteArr); fos.close(); System.out.println(1111); } }
当执行时,抛出如下异常:
Exception in thread "main" java.lang.RuntimeException: com.study.javassist.MyCC class is frozen at javassist.CtClassType.checkModify(CtClassType.java:286) at javassist.CtField.setModifiers(CtField.java:239) at com.study.javassist.ClassGeneratedByJavassist.main(ClassGeneratedByJavassist.java:36)
原因解释如下
当CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改。
因此,若想对CtClass对象进行修改,必须对其进行解冻,通过defrost()方法进行,如下所示:
public class ClassGeneratedByJavassist { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.study.javassist.MyCC"); // 添加一个参数 CtField ctField = new CtField(CtClass.intType, "id", ctClass); ctField.setModifiers(Modifier.PUBLIC); ctClass.addField(ctField); // 把生成的class文件写入文件 byte[] byteArr = ctClass.toBytecode(); FileOutputStream fos = new FileOutputStream(new File("D://MyCC.class")); fos.write(byteArr); fos.close(); System.out.println("over!!"); // 解冻CtClass对象 <span style="color:#ff0000;"><strong>ctClass.defrost(); </strong></span> // 为了测试ctClass是否能够再修改,再添加一个域 CtField ctField2 = new CtField(pool.get("java.lang.String"), "name", ctClass); ctField2.setModifiers(Modifier.PUBLIC); ctClass.addField(ctField2); byteArr = ctClass.toBytecode(); fos = new FileOutputStream(new File("D://MyCC.class")); fos.write(byteArr); fos.close(); System.out.println(1111); } }
通过反编译,结果如下:
package com.study.javassist; public class MyCC { public int id; public String name; public MyCC() { } }
类名操作
获取类名
ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.study.javassist.MyCC"); System.out.println(ctClass.getName());
结果如下:
com.study.javassist.MyCC
改变类名
ctClass.setName("com.study.javassist.MyCC2"); System.out.println(ctClass.getName());
结果:
com.study.javassist.MyCC2
通过重命名冻结类定义新类
当CtClass对象通过writeFile( )或者toBytecode( )方法转为class文件后,javassist不允许对CtClass对象作后续修改,因此当通过调用setName修改类名时是不允许的。
可以通过ClassPool的getAndRename(oldName, newName)方法实现。
如下示例:
ClassPool pool = ClassPool.getDefault(); CtClass ct1 = pool.get("com.study.javassist.TestName"); System.out.println(ct1.getName()); ct1.writeFile(); CtClass ct2 = pool.getAndRename("com.study.javassist.TestName", "com.study.javassist.TestName2"); System.out.println(ct2.getName());
结果如下:
com.study.javassist.TestName com.study.javassist.TestName2
javassist、ASM 对比
- javassist是基于源码级别的API比基于字节码的ASM简单。
- 基于javassist开发,不需要了解字节码的一些知识,而且其封装的一些工具类可以简单实现一些高级功能。比如HotSwaper。
- ASM比javassist性能更快,灵活行也较高。
- javassist提供者动态代理接口最慢,比JDK自带的还慢
参考:
http://blog.csdn.net/yyywyr/article/details/16984335
http://blog.csdn.net/z69183787/article/details/39343259
http://wsmajunfeng.iteye.com/blog/1912983
- 使用javassist生成新类
- 使用javassist生成新类
- Javassist 动态生成类
- 使用javassist生成实体对象
- 使用 Javassist 运行时生成泛型子类
- Javassist生成class(生成类,方法,字段,注解)
- javassist动态生成class
- javassist 动态生成WebService
- javassist使用实例
- javassist 介绍和使用
- javassist 的简单使用
- javassist 的使用
- java javassist使用实例
- javassist使用样例
- javassist使用样例
- Android 中使用Javassist
- javassist生成的对象回收
- 使用javassist代替反射完成类属性操作工具类
- Linux常用命令-VI与VIM编辑器介绍
- 2017暑假集训 div1 并查集(2)
- 冒泡排序(C语言版本)
- 文件上传路径问题
- jquery 使用index()方法 出现的问题
- 使用javassist生成新类
- 2017017(期末)
- C++设计模式——Bridge模式
- 《面向对象程序设计-C++》学习笔记2
- ES的集群名字被谁改了?
- volley学习笔记四
- BZOJ 3262 陌上花开 树状数组套splay
- 2 Add Two Numbers
- 17.7.11 校内赛 【图论】【最大流】【SPFA】