1. 位元組碼技術應用場景
AOP技術、Lombok去除重複代碼插件、動态修改class檔案等
2. 位元組技術優勢
Java位元組碼增強指的是在Java位元組碼生成之後,對其進行修改,增強其功能,這種方式相當于對應用程式的二進制檔案進行修改。Java位元組碼增強主要是為了減少備援代碼,提高性能等。
實作位元組碼增強的主要步驟為: 1、修改位元組碼
在記憶體中擷取到原來的位元組碼,然後通過一些工具(如 ASM,Javaasist)來修改它的byte[]數組,得到一個新的byte數組。 2、使修改後的位元組碼生效
有兩種方法:
1) 自定義ClassLoader來加載修改後的位元組碼;
2)替換掉原來的位元組碼:在JVM加載使用者的Class時,攔截,傳回修改後的位元組碼;或者在運作時,使用Instrumentation.redefineClasses方法來替換掉原來的位元組碼
3. 常見的位元組碼操作類庫
- BCEL
Byte Code Engineering Library(BCEL),這是Apache Software Foundation的Jakarta項目的一部分。BCEL是Java classworking 廣泛使用的一種架構,它可以讓您深入jvm彙編語言進行類庫操作的細節。BCEL與javassist有不同的處理位元組碼方法,BCEL在實際的jvm指令層次上進行操作(BCEL擁有豐富的jvm指令集支援) 而javassist所強調的是源代碼級别的工作。
- ASM
是一個輕量級Java位元組碼操作架構,直接涉及到JVM底層的操作和指令,高性能,高品質
- CGLB
生成類庫,基于ASM實作
- Javassist
是一個開源的分析,編輯和建立Java位元組碼的類庫。性能較ASM差,跟cglib差不多,但是使用簡單。很多開源架構都在使用它。1. Javassist優勢
– 比反射開銷小,性能高。
–javassist性能高于反射,低于ASM
運作時操作位元組碼可以讓我們實作如下功能:
– 動态生成 新的類
– 動态改變某個類的結構 ( 添加 / 删除 / 修改 新的屬性 / 方法 )
javassist 的最外層的 API 和 JAVA 的反射包中的 API 頗為 類似 。
它 主要 由 CtClass , CtMethod, ,以及 CtField 幾個類組成。用以執行和 JDK 反射 API 中 java.lang.Class, java.lang.reflect.Method, java.lang.reflect.Method .Field 相同的操作。
方法操作
– 修改已有方法的方法體(插入代碼到已有方法體)
– 新增方法 删除方法
2. Javassist的局限性
JDK5.0 新文法不支援 ( 包括泛型、枚舉 ) ,不支援注解修改,但可以通過底的 javassist 類來解決,具體參考: javassist.bytecode.annotation
不支援數組的初始化,如 String[]{"1","2"} ,除非隻有數組的容量為 1
不支援内部類和匿名類
不支援 continue 和 break表達式。
對于繼承關系,有些不支援。例如
class A {}
class B extends A {}
class C extends B {}
3. 使用Javassist建立類
//反射建立類 public static void main(String[] args)throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,SecurityException, IllegalArgumentException,InvocationTargetException { Class<?>clazz = Class.forName("com.itmayiedu.Test0005"); Object newInstance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sum", int.class, int.class); Object invoke = method.invoke(newInstance, 1, 1); } public void sum(int a, int b) { System.out.println("sum:" + a + b); } //javassist建立類 public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool = ClassPool.getDefault(); // 建立class檔案 CtClass userClass = pool.makeClass("com.itmayiedu.entity.User"); // 建立id屬性 CtField idField = CtField.make("private Integer id;", userClass); // 建立name屬性 CtField nameField = CtField.make("private Integer name;", userClass); // 添加屬性 userClass.addField(idField); // 添加屬性 userClass.addField(nameField); // 建立方法 CtMethod getIdMethod = CtMethod.make("public Integer getId() {return id;}", userClass); // 建立方法 CtMethod setIdMethod = CtMethod.make("public void setId(Integer id) { this.id = id; }", userClass); // 添加方法 userClass.addMethod(getIdMethod); // 添加方法 userClass.addMethod(setIdMethod); // 添加構造器 CtConstructor ctConstructor = new CtConstructor(new CtClass[] { CtClass.intType, pool.get("java.lang.String") },userClass); // 建立Body ctConstructor.setBody(" {this.id = id;this.name = name;}"); userClass.addConstructor(ctConstructor); userClass.writeFile("F:/test");// 将構造好的類寫入到F:\test 目錄下 }
4. Javassist修改類
public static void main(String[] args)throws NotFoundException,CannotCompileException, InstantiationException,IllegalAccessException,NoSuchMethodException,SecurityException, IllegalArgumentException, InvocationTargetException, IOException { ClassPool pool = ClassPool.getDefault(); // 需要加載類資訊 CtClass userClass = pool.get("com.itmayiedu.User"); // 需要添加的方法 CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{ CtClass.intType, CtClass.intType },userClass); // 方法權限 m.setModifiers(Modifier.PUBLIC); // 方法體内容 m.setBody("{System.out.println(\"Test003\"); return $1+$2;}"); userClass.addMethod(m); userClass.writeFile("F:/test");// 将構造好的類寫入到F:\test 目錄下 // 使用反射技術執行方法 Classclazz = userClass.toClass(); Object obj = clazz.newInstance(); // 通過調用User 無參構造函數 Method method = clazz.getDeclaredMethod("add", int.class, int.class); Object result = method.invoke(obj, 200, 300); System.out.println(result); }