1. 程式人生 > >位元組碼操作庫Javassist

位元組碼操作庫Javassist

一:建立新類:

package com.chen.Base_Points;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;

/**
 * 認識javassist的一些基本用法
 * 用其建立一個新類:
 * 1、獲得類池:ClassPool.getDefault();
 * 2、起名:makeClass()
 * 3、建立屬性:CtField
 * 4、建立方法:CtMethod
 * 5、建立構造器:CtConstructor
 * 建立好屬性、方法、構造器後要注意新增到類中:addXxx();
 * @author CHJ
 *
 */
public class Demo01 {

	public static void main(String[] args) throws Exception {
		
		// 獲得類池
		ClassPool pool = ClassPool.getDefault();
		// 建立新類
		CtClass cc = pool.makeClass("com.chen.Stu");//建立一個Stu類,其父目錄為com.chen為包名
		
		// 建立屬性
		CtField f1 = CtField.make("private String name;", cc);// 可以賦予初值
		CtField f2 = CtField.make("private int id;", cc);
		cc.addField(f1);
		cc.addField(f2);
		
		// 建立方法
		CtMethod  m1 = CtMethod.make("public String getName(){return name;}", cc);
		CtMethod  m2 = CtMethod.make("public void setName(String name){this.name = name;}", cc);
		cc.addMethod(m1);
		cc.addMethod(m2);
		
		
		// 建立構造器       與構建屬性和方法稍微有點不同
		// 構建帶參構造器,pool.get("java.lang.String")表示構造器第一個引數為String型別
		CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"), CtClass.intType}, cc);
		constructor.setBody("{this.name = name; this.id = id;}");// 建立方法時也可以這樣使用setBody()方法
		cc.addConstructor(constructor);
		// 建立無參構造器
		CtConstructor constructor2 = new CtConstructor(new CtClass[]{}, cc);
		constructor2.setModifiers(Modifier.PRIVATE);// 建立私有構造器,缺少時預設為public
		cc.addConstructor(constructor2);

		
		// 把它寫入一個檔案中
		cc.writeFile("F:/New");// 就在F:/New/com/chen下面生成一個Stu.class檔案,可以使用Xjad反編譯為一個java檔案
		System.out.println("建立新類成功");
	}
}

二:操作已存在的類

註解:

package com.chen.Base_Points;
public @interface Author { 
          String name(); 
           int year();
 }

package com.chen.Base_Points;
/**
 * 
 * @author CHJ
 *
 */
@Author(name = "虹君", year = 2015)
public class Emp {

	private String name;
	private int id;
	
	public Emp() {
		
	}

	public Emp(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void sayHello(int a) {
		
		System.out.println("你好:"+a);
	}
}

package com.chen.Base_Points;

import java.lang.reflect.Method;
import java.util.Arrays;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;

/**
 * 利用Javassist中的API,在位元組碼層面上操作Emp類
 * CtClass CtMethod CtField CtConstructor與
 * Class   Method   Field   Constructor
 * Javassist也可以像反射那樣,操作方法、屬性,構造器
 * 獲得相應的物件後就可以使用
 * insertBefore()
 * insertAfter()
 * insertAt()
 * 等方法對相應的屬性、方法、構造進行操作
 * @author Administrator
 *
 */
public class Operator_Emp {

	public static void main(String[] args) throws Exception {
		
		// 這裡02和03單獨執行沒有問題,但是如果同時執行就會報錯
//		test02();
		System.out.println("==================");
		test03();
	}
	
	/**
	 * 1、載入現成的java原始檔,檢視原始檔中的基本資訊
	 * @throws Exception
	 */
	public static void test01() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		
		// 獲得Emp類的一些資訊
		System.out.println(cc.getName());
		System.out.println(cc.getSimpleName());
		System.out.println(cc.getPackageName());
		System.out.println(cc.getInterfaces());
		System.out.println(cc.getSuperclass());
		// 把Emp轉換成位元組碼陣列
		byte[] bytes = cc.toBytecode();
		System.out.println(Arrays.toString(bytes));
	
	}
	
	
	/**
	 * 2、在Emp中新增一個方法
	 * 通過反射雖然能調到新增加的add方法,但在java原始檔中卻看不到add方法
	 * @throws Exception
	 */
	public static void test02() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		// CtMethod m = CtMethod.make("public int add(int a, int b){return a+b;}", cc);
		// 下面是另一種建立方法
		// 引數分別是:方法返回型別、方法名、引數型別、所在CtClass
		CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, cc);
		m.setModifiers(Modifier.PUBLIC);// 方法為public
		// 這裡不能寫為return a+b;因為a,b只是函式的形參值。在Javassist庫中$0代表this關鍵字 $1$2$3..代表實參值
		m.setBody("{return $1+$2;}");
		
		cc.addMethod(m);
		
		
		// 通過反射來呼叫新生成的方法
		Class clazz = cc.toClass();
		Emp emp = (Emp)clazz.newInstance();
		
		Method method = clazz.getDeclaredMethod("add", int.class, int.class);
		int result =  (int)method.invoke(emp, 200, 300);
		System.out.println(result);
	}
	
	/**
	 * 3、改變類中已有方法的資訊,修改方法體的結構
	 * @throws NotFoundException
	 */
	public static void test03() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		// 獲得Emp中已有的方法
		CtMethod m = cc.getDeclaredMethod("sayHello");
	
		// 改變方法體
		m.insertAt(40, "System.out.println(\"我在第四十行\");");
		m.insertBefore("System.out.println($1);System.out.println(\"start!!!!!!\");");// 在方法前插入
		m.insertAfter("System.out.println(\"end!!!!!!!\");");//  在方法後插入
		
		
		// 通過反射來調方法
		Class clazz = cc.toClass();
		Emp emp = (Emp)clazz.newInstance();
		
		Method method = clazz.getDeclaredMethod("sayHello", int.class);
		method.invoke(emp, 200);
		
	}
	
	/**
	 * 對類中屬性的一些操作
	 * @throws Exception
	 */
	public static void test04() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		// 建立新的屬性
		//CtField f = CtField.make("private double salary;", cc);
		// 另一種建立方法
		
		CtField f = new CtField(CtClass.doubleType, "salary", cc);
		f.setModifiers(Modifier.PUBLIC);
		cc.addField(f);
		
		// 建立set、get方法
		cc.addMethod(CtNewMethod.getter("getSalary", f));
		cc.addMethod(CtNewMethod.setter("setSalary", f));
		
		// 獲得已有屬性物件
		CtField f1 = cc.getDeclaredField("name");
	
	}
	
	/**
	 * 獲得構造器,改變構造器
	 * @throws Exception
	 */
	public static void test05() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		// 獲得所有的構造器,對構造其進行修改
		CtConstructor[] cons = cc.getConstructors();
		for ( CtConstructor temp : cons) {
			
			System.out.println(temp.getLongName());
			// 獲得構造器後就可以對它進行操作,如使用insertBefore()等方法
		}
	}
	
	/**
	 * 獲得註解
	 * @throws Exception
	 */
	public static void test06() throws Exception {
		
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get("com.chen.Base_Points.Emp");
		
		Object[] all = cc.getAnnotations();// Emp前面可能有多個註解
		
		Author a = (Author)all[0];// 因為這裡知道只有一個註解
		System.out.println("name:"+a.name()+"----year"+a.year());
		
		// 獲得方法、屬性的操作,需要先獲得方法、屬性物件,然後同上面一樣操作,獲得註解
	}
}