1. 程式人生 > >動態字節碼技術 javassist 初探

動態字節碼技術 javassist 初探

去除 object 包括 addm 高質量 動態字節碼 因此 操作 自定義

字節碼應用場景
AOP 技術、Lombok 去除重復代碼插件、動態修改 class 文件等
字節碼技術優勢
Java 字節碼增強指的是在 Java 字節碼生成之後,對其進行修改,增強其功能,這種方式相當於對應用程序的二進制文件進行修改,Java 字節碼增強主要是為了減少冗余代碼,提高性能等 實現字節碼增強的主要步驟為:
修改字節碼,在內存中獲取到原來的字節碼,然後通過一些工具(如 ASM,Javaasist)來修改它的byte[]數組,得到一個新的byte數組
使修改後的字節碼生效

自定義 ClassLoader 來加載修改後的字節碼
替換掉原來的字節碼,在 JVM 加載用戶的 Class 時,攔截,返回修改後的字節碼,或者在運行時,使用Instrumentation.redefineClasses 方法來替換掉原來的字節碼

常見的字節碼操作類庫
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 中最為重要的是 ClassPool、CtClass 、CtMethod、CtField
ClassPool:一個基於HashMap實現的 CtClass 對象容器,其中鍵是類名稱,值是表示該類的 CtClass 對象,默認的ClassPool 使用與底層 JVM 相同的類路徑,因此在某些情況下,可能需要向 ClassPool 添加類路徑或類字節
CtClass:表示一個類,這些 CtClass 對象可以從 ClassPool 獲得
CtMethods:表示類中的方法
CtFields :表示類中的字段
優勢:
比反射開銷小、性能高
操作字節碼可以動態生成新的類,動態修改類(添加、刪除、修改屬性或方法)
javassist 外層 API 和反射類似
主要有 CtClass、CtMethod、CtField 組成,執行反射中的 java.lang.Class、java.lang.reflect.Method、 java.lang.reflect.Method .Field 中的操作
劣勢:
不支持 JDK5 的新語法,包括泛型、枚舉,不支持註解修改
不支持數組初始化
不支持內部類和匿名類
不支持 continue 和 break
動態創建類
public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException {
ClassPool pool = ClassPool.getDefault();
// 創建User
CtClass userClass = pool.makeClass("com.kernel.entity.User");
// 創建屬性
CtField nameField = CtField.make("private String name;", userClass);
CtField ageField = CtField.make("private Integer age;", userClass);
// 添加屬性
userClass.addField(nameField);
userClass.addField(ageField);
// 創建方法
CtMethod getName = CtMethod.make("public String getName() {return name;}", userClass);
CtMethod setName = CtMethod.make("public void setName(String name) {this.name = name;}", userClass);
// 添加方法
userClass.addMethod(getName);
userClass.addMethod(setName);
// 創建構造器
CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"), pool.get("java.lang.Integer")}, userClass);
// 設置內容
constructor.setBody("{this.name = name;this.age = age;}");
// 添加構造器
userClass.addConstructor(constructor);
userClass.writeFile("D:\Codes\Java\Performance\jvm-day03\src\main\java\com\kernel\test");
}
動態添加並執行方法
public static void main(String[] args) {
try {
ClassPool pool = ClassPool.getDefault();
// 讀取com.kernel.User
CtClass userClass = pool.get("com.kernel.User");
// 創建方法,設置方法返回值
CtMethod method = new CtMethod(CtClass.voidType, "sum", new CtClass[] { CtClass.intType, CtClass.intType },
userClass);
// 設置方法內容
method.setBody("{System.out.println(\"sun:\" + ($1 + $2));}");
// 添加方法
userClass.addMethod(method);
userClass.writeFile("D:\Codes\Java\Performance\jvm-day03\src\main\java\com\kernel\test");
// 動態執行方法
Class clazz = userClass.toClass();
Object newInstance = clazz.newInstance();
// 獲得方法
Method sumMethod = clazz.getDeclaredMethod("sum", int.class, int.class);
sumMethod.invoke(newInstance, 2, 5);
} catch (Exception e) {
e.printStackTrace();
}
}

動態字節碼技術 javassist 初探