天天看點

java位元組碼技術包含類是如何通過位元組碼建立的

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);
}