什么是javassist,这个词一听起来感受就很懵,对吧~java
public void DynGenerateClass() { ClassPool pool = ClassPool.getDefault(); CtClass ct = pool.makeClass("com.ideaGenerateClass");//建立类 ct.setInterfaces(new CtClass[]{pool.makeInterface("java.lang.Cloneable")});//让类实现Cloneable接口 try { CtField f= new CtField(CtClass.intType,"id",ct);//得到一个类型为int,名称为id的字段 f.setModifiers(AccessFlag.PUBLIC);//将字段设置为public ct.addField(f);//将字段设置到类上 //添加构造函数 CtConstructor constructor=CtNewConstructor.make("public GeneratedClass(int pId){this.id=pId;}",ct); ct.addConstructor(constructor); //添加方法 CtMethod helloM=CtNewMethod.make("public void hello(String des){ System.out.println(des);}",ct); ct.addMethod(helloM); ct.writeFile();//将生成的.class文件保存到磁盘 //下面的代码为验证代码 Field[] fields = ct.toClass().getFields(); System.out.println("属性名称:" + fields[0].getName() + " 属性类型:" + fields[0].getType()); } catch (CannotCompileException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NotFoundException e) { e.printStackTrace(); } }
参考该篇文章java编程-javassist,编程
这里主要讲一下主要的几个类:app
ClassPool是CtClass对象的容器,它按需读取类文件来构造CtClass对象,而且保存CtClass对象以便之后使用。ide
从实现的角度来看,ClassPool 是一个存储 CtClass 的 Hash 表,类的名称做为 Hash 表的 key。ClassPool 的 get() 函数用于从 Hash 表中查找 key 对应的 CtClass 对象。若是没有找到,get() 函数会建立并返回一个新的 CtClass 对象,这个新对象会保存在 Hash 表中。函数
须要注意的是ClassPool会在内存中维护全部被它建立过的CtClass,当CtClass数量过多时,会占用大量的内存,API中给出的解决方案是从新建立ClassPool 或 有意识的调用CtClass的detach()方法以释放内存。this
//返回默认的ClassPool,通常经过该方法建立咱们的ClassPool; static ClassPool getDefault() //在搜索路径的开头插入目录或jar(或zip)文件。 ClassPath insertClassPath(java.lang.String pathname) //ClassPath在搜索路径的开头插入一个对象。 ClassPath insertClassPath(ClassPath cp) //获取类加载器toClass(),getAnnotations()在 CtClass等 java.lang.ClassLoader getClassLoader() //从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用。 CtClass get(java.lang.String classname) //将ClassPath对象附加到搜索路径的末尾。 ClassPath appendClassPath(ClassPath cp) //建立一个新的public类 CtClass makeClass(java.lang.String classname)
CtClass
类表示一个class文件,每一个CtClass对象
都必须从ClassPool
中获取,CtClass须要关注的方法:idea
//更改超类,除非此对象表示接口。 void setSuperclass(CtClass clazz) //将此类转换为java.lang.Class对象。 java.lang.Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup) //将该类转换为类文件。 byte[] toBytecode() //将由此CtClass 对象表示的类文件写入当前目录。 void writeFile() //将由此CtClass 对象表示的类文件写入本地磁盘。 void writeFile(java.lang.String directoryName) //在当前类中建立了一个静态代码块 CtConstructor makeClassInitializer()
freeze:冻结一个类,使其不可修改; isFrozen:判断一个类是否已被冻结; defrost:解冻一个类,使其能够被修改; prune:删除类没必要要的属性,以减小内存占用。调用该方法后,许多方法没法将没法正常使用,慎用; detach:将该class从ClassPool中删除; writeFile:根据CtClass生成.class文件; toClass:经过类加载器加载该CtClass; addField,removeField:添加/移除一个CtField; addMethod,removeMethod:添加/移除一个CtMethod; addConstructor,removeConstructor:添加/移除一个CtConstructor。
CtMethod
:表示类中的方法。spa
insertBefore:在方法的起始位置插入代码; insterAfter: 在方法的全部 return 语句前插入代码以确保语句可以被执行,除非遇到exception; insertAt: 在指定的位置插入代码; setBody: 将方法的内容设置为要写入的代码,当方法被abstract修饰时,该修饰符被移除; make: 建立一个新的方法。
CtConstructor的实例表示一个构造函数。它可能表明一个静态构造函数(类初始化器)。能够经过CtConstructor.make
方法建立.net
//设置构造函数主体。 void setBody(java.lang.String src) //从另外一个构造函数复制一个构造函数主体。 void setBody(CtConstructor src, ClassMap map) //复制此构造函数并将其转换为方法。 CtMethod toMethod(java.lang.String name, CtClass declaring)
该类做用是用于经过 getResourceAsStream() 在 java.lang.Class 中获取类文件的搜索路径。线程
构造方法:
//建立一个搜索路径。 ClassClassPath(java.lang.Class<?> c)
//获取指定类文件的URL。 java.net.URL find (java.lang.String classname) //经过获取类文getResourceAsStream()。 java.io.InputStream openClassfile(java.lang.String classname)
ClassPool pool = ClassPool.getDefault();
在默认系统搜索路径获取ClassPool
对象。
若是须要修改类搜索的路径须要使用insertClassPath
方法进行修改。
pool.insertClassPath(new ClassClassPath(this.getClass()));
将本类所在的路径插入到搜索路径中
package com.demo; import javassist.*; import java.io.IOException; import java.util.Arrays; public class testssit { public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(demo.class.getClass())); CtClass ctClass = pool.get("com.demo.test"); ctClass.setSuperclass(pool.get("com.demo.test")); // System.out.println(ctClass); byte[] bytes = ctClass.toBytecode(); String s = Arrays.toString(bytes); System.out.println(s); } }
toClass:将修改后的CtClass加载至当前线程的上下文类加载器中,CtClass的toClass方法是经过调用本方法实现。
Hello类: public class Hello { public void say() { System.out.println("Hello"); } } Test 类 public class Test { public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault();//在默认系统搜索路径获取ClassPool对象。 CtClass cc = cp.get("com.demo.Hello"); //获取hello类的 CtMethod m = cc.getDeclaredMethod("say"); //获取hello类的say方法 m.insertBefore("{ System.out.println(\"Hello.say():\"); }");//在正文的开头插入字节码 Class c = cc.toClass();//将此类转换为java.lang.Class对象 Hello h = (Hello)c.newInstance(); //反射建立对象并进行强转 h.say();调用方法say } }
public class App { public static void main(String[] args) { try { createPerson(); } catch (Exception e) { e.printStackTrace(); } } private static void createPerson() throws Exception { ClassPool pool = ClassPool.getDefault(); // 1. 建立一个空类 CtClass cc = pool.makeClass("com.hearing.demo.Person"); // 2. 新增一个字段 private String name = "hearing"; CtField param = new CtField(pool.get("java.lang.String"), "name", cc); param.setModifiers(Modifier.PRIVATE); cc.addField(param, CtField.Initializer.constant("hearing")); // 3. 生成 getter、setter 方法 cc.addMethod(CtNewMethod.setter("setName", param)); cc.addMethod(CtNewMethod.getter("getName", param)); // 4. 添加无参的构造函数 CtConstructor cons = new CtConstructor(new CtClass[]{}, cc); cons.setBody("{name = \"hearing\";}"); cc.addConstructor(cons); // 5. 添加有参的构造函数 cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc); // $0=this / $1,$2,$3... 表明方法参数 cons.setBody("{$0.name = $1;}"); cc.addConstructor(cons); // 6. 建立一个名为printName方法,无参数,无返回值,输出name值 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("{System.out.println(name);}"); cc.addMethod(ctMethod); //这里会将这个建立的类对象编译为.class文件 cc.writeFile("../"); } }
建立的class文件以下
public class Person { private String name = "hearing"; public void setName(String var1) { this.name = var1; } public String getName() { return this.name; } public Person() { this.name = "hearing"; } public Person(String var1) { this.name = var1; } public void printName() { System.out.println(this.name); } }
Object person = cc.toClass().newInstance(); Method setName = person.getClass().getMethod("setName", String.class); setName.invoke(person, "hearing1"); Method execute = person.getClass().getMethod("printName"); execute.invoke(person);
ClassPool pool = ClassPool.getDefault(); // 设置类路径 pool.appendClassPath("../"); CtClass ctClass = pool.get("com.hearing.demo.Person"); Object person = ctClass.toClass().newInstance(); // 下面和经过反射的方式同样去使用