位元組碼操作庫Javassist
阿新 • • 發佈:2019-02-04
一:建立新類:
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()); // 獲得方法、屬性的操作,需要先獲得方法、屬性物件,然後同上面一樣操作,獲得註解 } }