天天看點

JVM-動态位元組碼技術詳解1位元組碼技術應用場景4.使用Javassist建立類5.使用Javassist修改一個類

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