字節碼操做java
反射git
動態生成新的類github
動態改變某個類的結構(添加/刪除/修改 新的屬性/方法)編程
比反射開銷小,性能高數組
JAVAasist性能高於反射,低於asmapp
Byte Code Engineering Library (BCEL), 這是Apache Software Foundation 的 Jakarta 項目的一部分.BCEL是Java classworking普遍使用的一種框,它可讓您深刻JVM彙編語言進行類操做的細節.BCEL與Javassist有不一樣的處理字節碼方法,BCEL在實際的JVM指令層次上進行操做(BCEI擁有豐富的JVM指令級支持)而Javassist所強調的是源代碼級別的工做框架
是一個輕量級ava字節碼操做框架,直接涉及量到VM底層的操做和指令ide
是一個強大的,高性能,高質量的Code生成類庫,基於ASM實現工具
是一個開源的分析、編輯和建立Jaw字節碼的類庫.性能較ASM差,跟cglib差很少,可是使用簡單.不少開源框架都在使用它性能
Unlike other similar bytecode editors,Javassist provides two levels of API:source level and bytecode level.
使用前先導入javassist的jar包
Demo:
/** * 使用javassist生成一個新的類 * @author Matrix42 * */ public class Demo01 { public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("com.lorinda.bean.Emp"); //建立屬性 CtField f1 = CtField.make("private int empno;", cc); CtField f2 = CtField.make("private String ename;", cc); cc.addField(f1); cc.addField(f2); //建立方法 CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc); CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc); cc.addMethod(m1); cc.addMethod(m2); CtMethod m3 = CtMethod.make("public String getEname(){return ename;}", cc); CtMethod m4 = CtMethod.make("public void setEname(String empno){this.ename = ename;}", cc); cc.addMethod(m3); cc.addMethod(m4); //添加構造器 CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc); constructor.setBody("{this.empno=$1;this.ename=$2;}"); cc.addConstructor(constructor); //將上面構造好的類寫入到d:/myjava cc.writeFile("d:/myjava"); System.out.println("生成類,成功!"); } }
建立完成後使用XJAD反編譯就能夠看到源碼了
反編譯源碼:
package com.lorinda.bean; public class Emp { private int empno; private String ename; public int getEmpno() { return empno; } public void setEmpno(int i) { empno = i; } public String getEname() { return ename; } public void setEname(String s) { ename = ename; } public Emp(int i, String s) { empno = i; ename = s; } }
方法操做
修改已有方法的方法體(插入代碼到已有方法體)
新增方法
刪除方法
a | b | c |
---|---|---|
\$0,\$1,\$2,... | this and actual parameters | \$0 表明的是 this,\$1 表明方法參數的第一個參數,\$2 表明方法參數的第二個參數, 以此類推,$N 表明方法參數的第 N 個參數 |
\$args | An arrar of parameters | The type of $args is Object[], \$args[0] 對應的是 \$1 而不是 \$0 |
$$ | 全部方法參數的簡寫, 主要用在方法調用上 | move(String a,String b) move(\$\$) 至關於 move(\$1,\$2) |
fallthrough | path | 在類路徑, 源文件路徑等中有不存在的路徑警告 |
\$cflow | ||
\$r | ||
\$_ | ||
addCatch() | ||
\$class | ||
\$sig |
修改已有方法的方法體(插入代碼到已有方法體)
新增方法
刪除方法
Demo:
import java.awt.color.CMMException; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; public class Demo02 { public static void test01() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); byte[] bytes = cc.toBytecode(); System.out.println(Arrays.toString(bytes)); System.out.println(cc.getName()); //得到類名 System.out.println(cc.getSimpleName()); //得到簡要類名 System.out.println(cc.getSuperclass()); //得到父類 System.out.println(cc.getInterfaces()); //得到接口 } public static void test02()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); //CtMethod m = CtMethod.make("public int add(int a,int b){return a+b;}", cc); CtMethod m = new CtMethod(CtClass.intType,"add", new CtClass[]{CtClass.intType,CtClass.intType},cc); m.setModifiers(Modifier.PUBLIC); m.setBody("{System.out.println(\"Ha Ha\");return $1+$2;}"); cc.addMethod(m); //經過反射調用新生產的方法 Class<?> clazz = cc.toClass(); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("add", int.class,int.class); Object result = method.invoke(obj, 200,300); System.out.println(result); } public static void test03()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType}); cm.insertBefore("System.out.println($1);"); Class<?> clazz = cc.toClass(); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sayHello", int.class); method.invoke(obj, 90); } public static void test04() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); //CtField f1 = CtField.make("private int empno", cc); CtField f1 = new CtField(CtClass.intType,"salary",cc); f1.setModifiers(Modifier.PRIVATE); cc.addField(f1,"1000");//1000位默認值 // cc.getDeclaredField("ename"); //獲取指定屬性 cc.addMethod(CtNewMethod.getter("salary",f1)); cc.addMethod(CtNewMethod.setter("salary", f1)); } public static void test05()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); CtConstructor[] cs = cc.getConstructors(); for (CtConstructor ctConstructor : cs) { System.out.println(ctConstructor.getLongName()); } } public static void main(String[] args) throws Exception { //test01(); //test02(); // test03(); // test04(); test05(); } }
getConstructors()
代碼片斷:
public @interface Author { String name(); int year(); }
@Author(name="Chiba",year=2005) public class Point{ int x,y; }
CtClass cc = ClassPool.getDefault.get("Point"); Object[] all = cc.getAnnotations(); Author a = (Author)all[0]; String name = a.name(); int year = a.year(); System.out.println("name:"+name+",year:"+year);
當調用了writeFile(),toClass(),toBytecode(),Javassist會把那個CtClass對象凍結,若是想使用凍結的對象能夠調用.defrose()方法
侷限性
JDK5.0新語法不支持(包括泛型,枚舉),不支持註解修改,單能夠的經過底層javasist類來解決,具體參考:javassist.bytecode.annotation
不支持數組的初始化,如String[]{"1","2"},除非只有數組容量爲1
不支持內部類盒匿名類
不支持continue盒break表達式
對於繼承關係,有些語法不支持,如: