什麼是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(); // 下面和經過反射的方式同樣去使用