1. 程式人生 > >Java學習之動態編譯--位元組碼操作--javassist類庫

Java學習之動態編譯--位元組碼操作--javassist類庫

一、位元組碼操作

1.Java動態性的兩種常見實現方式:

  • 位元組碼操作
  • 反射

2.執行時操作位元組碼可以實現如下功能:

  • 動態生成新的類
  • 動態改變某個類的結構(新增/刪除/修改 新的屬性/方法)

3.優勢:

  • 比反射開銷小,效能高
  • Javaasist效能高於反射,低於ASM

二、常見的位元組碼操作類庫

1.BCEL

Byte Code Engineering Library(BCEL),這是Apache Software Foundation的Jakarta專案的一部分。BCEL是Java classworking 廣泛使用的一種框架,它可以讓您深入jvm組合語言進行類庫操作的細節。BCEL與javassist有不同的處理位元組碼方法,BCEL在實際的jvm指令層次上進行操作(BCEL擁有豐富的jvm指令集支援) 而javassist所強調的是原始碼級別的工作。

2.ASM

是一個輕量級Java位元組碼操作框架,直接涉及到JVM底層的操作和指令
高效能,高質量

3.CGLB(code generation library)

 生成類庫,基於ASM實現

4.javassist

是一個開源的分析,編輯和建立Java位元組碼的類庫。效能較ASM差,跟cglib差不多,但是使用簡單。很多開源框架都在使用它。
主頁: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/

三、javassist庫的API詳解

  • javassist的最外層的API和java的反射包中的API及其類似
  • 它主要有CtClass, CtMethod,CtField幾個類組成,用於執行和JDK反射API中java.lang.Class, java.lang,reflect.Method,java.lang.reflect.Method.Field相同的操作

(1):建立一個類

程式碼:

public class JavassistTest {
	public static void main(String[] args) throws Exception {
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.makeClass("bean.User");
		
		//建立屬性
		CtField field01 = CtField.make("private int id;",cc);
		CtField field02 = CtField.make("private String name;", cc);
		cc.addField(field01);
		cc.addField(field02);

		//建立方法
		CtMethod method01 = CtMethod.make("public String getName(){return name;}", cc);
		CtMethod method02 = CtMethod.make("public void setName(String name){this.name = name;}", cc);
		cc.addMethod(method01);
		cc.addMethod(method02);
		
		//新增有參構造器
		CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")},cc);
		constructor.setBody("{this.id=id;this.name=name;}");
		cc.addConstructor(constructor);
		//無參構造器
		CtConstructor cons = new CtConstructor(null,cc);
		cons.setBody("{}");
		cc.addConstructor(cons);
		
		cc.writeFile("E:/workspace/TestCompiler/src");
	}

}

注意:無參構造器和有參構造器


(2):方法操作:

  • 修改已有的方法體(插入程式碼到已有方法體)
  • 新增方法
  • 刪除方法
$0 $1 $2  $0代表是this, $1代表方法引數的第一個引數,$2代表方法引數的第二個引數,以此類推,$N代表方法引數的第N個
$args The type of $args is OBject[]. $args(0)對應的是$1,不是$0
$$ 一個方法呼叫的深度
$r 方法返回值的型別
$_ 方法返回值。(修改方法體時不支援)
addCatch() 方法中加入try catch塊  $e代表 異常物件
$class this的型別(Class)。也就是$0的型別
$sig 方法引數的型別(Class)陣列,陣列的順序。

(3):屬性操作

(4):構造方法操作

getConstructors()

(5)註解操作

public @interface Author{
String name();
int year();
}

@Author(name="over",year=2012)
public class Point{int x,int y;}

CtClass cc=ClassPool.getDefault().get("Point");
Object[] all = cc.getAnnotations();
Author a =(Author)all[0];
String name = a.name();
int year = a.year();
System.out.println(name+":"+year);


四、侷限性:

  1. JDK5.0新語法不支援(包括泛型,列舉),不支援註解修改,但可以通過底層的javassist類來解決,具體參考:javassist,bytecode.annotation
  2. 不支援陣列的初始化,如String[]{"a","b"},除非只有陣列的容量為1
  3. 不支援內部類和匿名類
  4. 不支援continue和break 表示式
  5. 對於繼承關係,有些不支援 。例如
class A{}
class B extends A{}
Class C extends B{}


查資料: javassist與反射的效能比較

Demo:

public class Demo01 {
	//獲取類的簡單資訊
	public static void test01() throws Exception{
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("bean.User");
		
		//得到位元組碼
		byte[] bytes = cc.toBytecode();
		System.out.println(Arrays.toString(bytes));
	
		System.out.println(cc.getName());//獲取類名
		System.out.println(cc.getSimpleName());//獲取簡要類名
		System.out.println(cc.getSuperclass());//獲取父類
		System.out.println(cc.getInterfaces());//獲取介面
		System.out.println(cc.getMethods());//獲取
	}
	
	//新生成一個方法
	public static void test02() throws Exception{
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("bean.User");
		
		//第一種
		//CtMethod cm = CtMethod.make("public String getName(){return name;}", cc);
		//第二種
		//引數:返回值型別,方法名,引數,物件
		CtMethod cm = new CtMethod(CtClass.intType,"add",new CtClass[]{CtClass.intType,CtClass.intType},cc);
		cm.setModifiers(Modifier.PUBLIC);//訪問範圍
		cm.setBody("{return $1+$2;}");
		
		//cc.removeMethod(m) 刪除一個方法
		cc.addMethod(cm);
		//通過反射呼叫方法
		Class clazz = cc.toClass();
		Object obj = clazz.newInstance();//通過呼叫無參構造器,生成新的物件
		Method m = clazz.getDeclaredMethod("add", int.class,int.class);
		Object result = m.invoke(obj, 2,3);
		System.out.println(result);
	}
	
	//修改已有的方法
	public static void test03() throws Exception{
		ClassPool pool  = ClassPool.getDefault();
		CtClass cc = pool.get("bean.User");
		
		CtMethod cm = cc.getDeclaredMethod("hello",new CtClass[]{pool.get("java.lang.String")});
		cm.insertBefore("System.out.println(\"呼叫前\");");//呼叫前
		cm.insertAt(29, "System.out.println(\"29\");");//行號
		cm.insertAfter("System.out.println(\"呼叫後\");");//呼叫後
		
		//通過反射呼叫方法
		Class clazz = cc.toClass();
		Object obj = clazz.newInstance();
		Method m = clazz.getDeclaredMethod("hello", String.class);
		Object result = m.invoke(obj, "張三");
		System.out.println(result);		
	}
	
	//修改已有屬性
	public static void test04() throws Exception{
		ClassPool pool  = ClassPool.getDefault();
		CtClass cc = pool.get("bean.User");
		
		//屬性
		CtField cf = new CtField(CtClass.intType,"age",cc);
		cf.setModifiers(Modifier.PRIVATE);
		cc.addField(cf);
		//增加響應的get set方法
		cc.addMethod(CtNewMethod.getter("getAge",cf));
		cc.addMethod(CtNewMethod.setter("setAge",cf));
		
		//訪問屬性
		Class clazz = cc.toClass();
		Object obj = clazz.newInstance();		
		Field field = clazz.getDeclaredField("age");
		System.out.println(field);
		Method m = clazz.getDeclaredMethod("setAge", int.class);
		m.invoke(obj, 16);
		Method m2 = clazz.getDeclaredMethod("getAge", null);
		Object resutl = m2.invoke(obj,null);		
		System.out.println(resutl);
	}
	
	//操作構造方法
	public static void test05() throws Exception{
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("bean.User");
		
		CtConstructor[] cons = cc.getConstructors();
		for(CtConstructor con:cons){
			System.out.println(con);
		}
	}
	public static void main(String[] args) throws Exception {
		//test01();
		//test02();
		//test03();
		//test04();
		test05();
	}

}

相關推薦

Java學習動態編譯--位元組操作--javassist

一、位元組碼操作 1.Java動態性的兩種常見實現方式: 位元組碼操作反射 2.執行時操作位元組碼可以實現如下功能: 動態生成新的類動態改變某個類的結構(新增/刪除/修改 新的屬性/方法) 3.優勢: 比反射開銷小,效能高Javaasist效能高於反射,低於ASM 二、常見

2018年10月31日Java學習常用的命令列操作

1.常用的命令列操作 win+r調出並輸入cmd. 進入D盤:D:回車進入D盤:D:回車 D盤下的目錄DIR回車 建立目錄md 2018年10月31日 那麼建立了一個2018年10月31日的目錄(目前還在d盤目錄) 進入2018年10月31日的目錄 cd

【1.1】Eigen C++ 矩陣開源學習稠密矩陣和陣列操作——矩陣

稠密矩陣和陣列操作 http://eigen.tuxfamily.org/dox-devel/group__DenseMatrixManipulation__chapter.html 包含模組: 1.矩陣類 2.矩陣和向量的運算

8.5(java學習筆記)8.5 位元組操作javassist

一、javassist   javassist讓我們操作位元組碼更加簡單,它是一個類庫,允許我們修改位元組碼。它允許java程式動態的建立、修改類。   javassist提供了兩個層次的API,基於原始碼級別的和位元組碼級別的。     二、javassist建立類   1.獲取類池

Android中使用Java開源Javassist動態建立位元組學習研究

研究該內容想要達到的目的: 通過Javassist動態建立位元組碼特性在Android專案中執行時生成.dex檔案供Android程式呼叫。 Javassist簡介: Javassist是一個開源的分析、編輯和建立Java位元組碼的類庫。它是針對JAV

java學習路--繼承(多態的動態綁定)

程序 有一個 完全 報錯 參數類型 其中 完全匹配 一個 執行過程 動態綁定過程中,對象調用對象方的執行過程   1:編譯器查看對象的聲明類型和方法名。有可能有多個方法名相同,但參數類型不一樣的重載方法。   2:編譯器查看調用方法時提供的參數類型。該過程叫重載解析,在相同

java反射動態代理學習筆記

ace ins 功能 運行 invoke -- ram lang glib 動態代理概述:代理:本來自己做的事情,請別人來做,被請的人就是代理對象;舉例:春節回家買票讓人代理買動態代理:在程序運行過程中產生的這個對象,而程序運行過程中產生對象其實就是我們剛才反射講解的內容,

21 Java學習位元組流(InputStream和OutPutStream)

  一.流的分類   1、從功能上:輸入流、輸出流 2、從結構上:位元組流、字元流 3、從來源上:節點流、過濾流   其中InputStream/OutputStream是為位元組流而設計的,Reader/Writer是為字元流而設計的。處理位元組或者二進位制物件使用位元組

java學習代理(2):靜態代理和動態代理

一,代理的概念 代理是一個物件,代理物件為其他物件提供一種代理,以控制對這個物件的訪問,代理物件起到中介作用,可以去掉或者增加額外的服務。 如:火車票代售點就是火車站售票處的一個代理物件,可通過訪問代售點進行業務處理。 二,靜態代理的2種實現方式:繼承和聚合 靜態代理中的代

java的動態性------Java位元組操作

常見的位元組碼操作類庫BECL :是java classing廣泛使用的一種框架,可以深入理解JVM組合語言,難學,需要一些JVM底層指令ASM :輕量級的java位元組碼操作框架,直接涉及JVM底層操作和指令CGLIB  :是基於ASM的的實現,強大效能高Javassist

深入理解Java虛擬機器虛擬機器位元組執行引擎

執行引擎是java虛擬機器最核心的組成部分之一。 物理機的執行引擎是建立在處理器、硬體、指令集和作業系統層面上的,而虛擬機器的執行引擎是由自己實現的,可以自行制定指令集與執行引擎的結構體系,並且能夠執行那些硬體不直接支援的指令集格式。 執行引擎在執行Java

位元組操作_javassist_動態建立新_屬性_方法_構造器_API詳解JAVA216-217

來源:http://www.bjsxt.com/ 一、S02E216_01位元組碼操作_javassist庫、介紹、動態建立新類、屬性、方法、構造器 位元組碼操作 常見的位元組碼操作類庫 JAVAssist庫 package com

java 位元組操作javassist

用javassist生成一個類(位元組碼檔案) /** * 使用javassist生成一個新的類 * @author L J */ public class JavassistDemo { public static void main(Stri

JAVA獲得版本號以及位元組編譯版本

公司的開發環境比較老,尋找一些jar包的時候總是會糾結對應的編譯版本,感覺很麻煩,所以寫了一個工具類用於讀取class或jar檔案的編譯版本,供大家參考。 package com.jinggujin.util; import java.io.DataIn

JAVA虛擬機器學習總結——虛擬機器位元組執行引擎

執行時的棧幀結構 棧幀是用於支援虛擬機器進行方法呼叫和方法執行的資料結構,它是虛擬機器執行時資料區中的虛擬機器棧的棧元素。 棧幀儲存了方法的區域性變量表,運算元棧,動態連線和方法返回值等資訊每一個方法從呼叫至執行完成的過程,都對應者棧幀在虛擬機器棧裡面從入找

Java位元組操作Javassist概述

Javassist(Java Programming Assistant)是一款編輯Java位元組碼的類庫。能夠在執行時定義新的Java類,在JVM載入類檔案時修改類的定義。 Javassist類庫提

Java位元組詳解系列二--解析位元組

1、javap檢視位元組碼內容上文介紹了位元組碼的結構,本文主要通過一個簡單的例子說明class位元組碼的每一個欄位。package com.zcm.test; import java.io.Serializable; public class SourceTest im

Java學習jdk與cglib動態代理

宣告:參考部分部落格做記錄 1.jdk動態代理實現 介面: public interface UserService { String getName(); } 介面實現: public class UserServiceImpl impl

JAVA 位元組操作利器javassist

1、簡介 javassist是一個開源的分析、編輯和建立java位元組碼的類庫。不需要了解虛擬機器指令,就能動態生成類或者改變類的結構。 2、下載 (2)使用的版本是javassist-3.18.0-GA。 Javassist是一個執行位元組碼操作的強而有力

關於360外掛化Replugin Activity動態修改父位元組操作

開發十年,就只剩下這套架構體系了! >>>