概述
java動態位元組碼指的是在java位元組碼生成之後,對其進行修改,增強其功能,這種方式相當於對程式碼的二進位制檔案進行修改。動態java位元組碼主要是為了減少冗餘程式碼,提高效能。
實現位元組碼增強的主要步驟:
- 修改位元組碼。在記憶體中獲取到原來的位元組碼,通過一些工具(如
ASM
、Javaasist
)來修改它的byte[]陣列,得到一個新的byte[]陣列。 - 使修改後的位元組碼生效
- 自定義
ClassLoader
來載入修改後的位元組碼 - 替換掉原來的位元組碼,在
JVM
載入使用者的Class
時,攔截返回修改後的位元組碼。
- 自定義
用途
- 動態生成新的類
- 動態修改某個類的結構(新增/刪除/修改 新的屬性/方法)
- AOP技術使用的就是動態位元組碼技術
位元組碼操作類庫
BCEL
BCEL
可以深入JVM
組合語言進行類操作的細節。BCEL
與javassist
有不同的處理位元組碼方法,BCEL
在實際的JVM
指令層次上進行操作,而javassist
所強調的是原始碼級別的工作。
ASM
是輕量級java
位元組碼操作框架,直接涉及到JVM
底層的操作和指令。高效能,高質量
CGLB
基於ASM
實現
javassist
效能低於ASM
跟CGLB
差不多,但是使用簡單,很多開源框架都使用的是javassist
。javassist
比反射開銷小,效能高。
javassist
的最外層的API
和JAVA
的反射包中的API
頗為相似。它主要由CtClass
、CtMethod
以及CtField
幾個類組成,用以執行和JDK
反射API
中java.lang.Class
、java.lang.reflect.Method
、java.lang.reflect.Field
相同的操作。
侷限性
JDK5.0
新語法不支援(包括泛型、列舉),不支援註解修改- 不支援陣列的初始化
- 不支援內部類和匿名類
- 不支援
continue
和break
表示式 - 對於繼承關係,有些不支援。
maven : mvnrepository.com/artifact/ja…
反射呼叫方法
public class User {
private String name;
private Integer age;
public void sum(int a,int b){
int sum = a + b;
System.out.println("sum = " + sum);
}
public static void main(String[] args) {
try {
Class<?> clz = Class.forName("javassist.User");
Object newInstance = clz.newInstance();
Method method = clz.getDeclaredMethod("sum", int.class, int.class);
Object invoke = method.invoke(newInstance, 1, 3);
}catch (Exception e){
}
}
}
複製程式碼
javassist
生成class
檔案
//使用javassist建立class檔案
ClassPool pool = ClassPool.getDefault();
//建立 class 檔案
CtClass userClass = pool.makeClass("com.beisiji.javassist.User");
//建立 id 屬性
CtField idField = CtField.make("private Integer id;", userClass);
//建立 name 屬性
CtField nameField = CtField.make("private String name;", userClass);
//新增屬性
userClass.addField(idField);
userClass.addField(nameField);
//建立方法
CtMethod setIdMethod = CtMethod.make("public void setId(Integer id) { this.id = id; }", userClass);
CtMethod getIdMethod = CtMethod.make("public Integer getId() { return id; }", userClass);
//新增方法
userClass.addMethod(setIdMethod);
userClass.addMethod(getIdMethod);
//建立構造器
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, userClass);
ctConstructor.setBody("{ this.id = id;this.name = name; }");
userClass.addConstructor(ctConstructor);
userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
複製程式碼
javassist
修改類檔案資訊
//使用javassist修改類檔案資訊(新增方法)
ClassPool pool = ClassPool.getDefault();
//需要載入的類資訊(需要修改類資訊的全限定名稱)
CtClass userClass = pool.get("executor.ExecutorDemo");
//建立方法
CtMethod method = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, userClass);
//設定方法許可權
method.setModifiers(Modifier.PUBLIC);
method.setBody("{return $1 + $2;}"); //$0:this , $1第一個引數 , $2第二個引數
userClass.addMethod(method);
userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
//呼叫新增的方法
Class clz = userClass.toClass();
Object newInstance = clz.newInstance();
Method addMethod = clz.getDeclaredMethod("add", int.class, int.class);
Object invoke = addMethod.invoke(newInstance, 2, 3);
System.out.println("invoke = " + invoke);
複製程式碼