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差不多,但是使用簡單。很多開源架構都在使用它。
Javassist優勢
– 比反射開銷小,性能高。
–javassist性能高于反射,低于ASM
運作時操作位元組碼可以讓我們實作如下功能:
– 動态生成 新的類
– 動态改變某個類的結構( 添加/ 删除/ 修改 新的屬性/ 方法)
javassist的最外層的API 和JAVA 的反射包中的API 頗為 類似 。
它 主要 由CtClass , CtMethod, ,以及CtField 幾個類組成。用以執行和JDK 反射API 中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method .Field 相同的 操作 。
方法操作
– 修改已有方法的方法體(插入代碼到已有方法體)
– 新增方法 删除方法
javassist的局限性
JDK5.0 新文法不支援 ( 包括泛型、枚舉 ) ,不支援注解修改,但可以通過底層的 javassist 類來解決,具體參考: javassist.bytecode.annotation
不支援數組的初始化,如 String[]{"1","2"} ,除非隻有數組的容量為 1
不支援内部類和匿名類
不支援 continue 和 break表達式。
對于繼承關系,有些不支援。例如
class A {}
class B extends A {}
class C extends B {}
4.使用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);
}
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 目錄下
}
5.使用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 目錄下
// 使用反射技術執行方法
Class clazz = 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);
}