常見的位元組碼操作類庫
https://github.com/jboss-javassist/javassist
JAVAssist的API詳解
Intellij IDEA 添加jar包的三種方式
用javassist生成一個新的類
用javassist生成一個新的類
import javassist.*;
/**
* 測試用javassist生成一個新的類
*/
public class Demo01 {
public static void main(String[] args) throws Exception{
// 擷取類池
ClassPool pool = ClassPool.getDefault();
// 建立新類
CtClass cc = pool.makeClass("test.bean.Emp");
// 建立屬性
CtField f1 = CtField.make("private int empno;", cc);
CtField f2 = CtField.make("private String ename;", cc);
cc.addField(f1);
cc.addField(f2);
// 建立方法
CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc);
cc.addMethod(m1);
cc.addMethod(m2);
// 添加構造器
CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, cc);
constructor.setBody("{this.empno=empno; this.ename=ename;}");
cc.addConstructor(constructor);
cc.writeFile("./"); // 将上面建立好的類寫入指定檔案夾下
System.out.println("Successful !!!");
}
}
生成.class檔案
XJad反編譯工具
找到位元組碼所在目錄打開即可
JAVAssist庫的API詳解
test.Emp
package test;
public class Emp {
private int empno;
private String ename;
public void sayHello(int a){
System.out.println("sayHello " + a);
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Emp(int empno, String ename) {
this.empno = empno;
this.ename = ename;
}
public Emp(){
}
}
Demo
package test;
import javassist.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 測試用javassist API
*/
public class Demo02 {
/**
* 處理類的基本用法
*/
public static void test01() throws Exception{
ClassPool pool = ClassPool.getDefault();
// 加載已有類
CtClass cc = pool.get("test.Emp");
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()); // 獲得接口,得到的是一個數組
}
/**
* 測試添加新的方法
*/
public static void test02() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Emp");
// CtMethod m = CtNewMethod.make("public int add(int a, int b){return a+b;}", cc);
// 聲明一個方法
// (傳回類型, 方法名, 參數類型, 添加到哪個類)
CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, cc);
// 添加方法體
m.setModifiers(Modifier.PUBLIC); // 通路權限
m.setBody("{System.out.println(\"add fun\"); return $1 + $2;}");
cc.addMethod(m);
// 通過反射調用新生成的方法
Class clz = cc.toClass();
Object obj = clz.getConstructor().newInstance(); // 建立Emp對象
Method method = clz.getDeclaredMethod("add", int.class, int.class);
Object result = method.invoke(obj, 200, 300);
System.out.println(result);
}
/**
* 修改已有方法的資訊,修改方法體的内容
*/
public static void test03() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Emp");
CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});
cm.insertBefore("System.out.println($1); System.out.println(\"start !!!\");");
// 在原檔案的指定行數添加代碼
cm.insertAt(9, "int b = 3;System.out.println(\"b = \" + b);");
cm.insertAfter("System.out.println(\"end !!!\");");
// 通過反射調用新生成的方法
Class clz = cc.toClass();
Object obj = clz.getConstructor().newInstance(); // 建立Emp對象
Method method = clz.getDeclaredMethod("sayHello", int.class);
method.invoke(obj, 300);
}
/**
* 屬性的操作
*/
public static void test04() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Emp");
// CtField f1 = CtField.make("private int salary = 1000;", cc);
CtField f1 = new CtField(CtClass.intType,"salary",cc);
f1.setModifiers(Modifier.PRIVATE);
cc.addField(f1, "1000");
// 增加相應的set/get方法
cc.addMethod(CtNewMethod.getter("getSalary", f1));
cc.addMethod(CtNewMethod.setter("setSalary", f1));
CtField f2 = cc.getDeclaredField("salary"); // 擷取指定屬性
System.out.println(f2.getName());
// 反射
Class clz = cc.toClass();
// Emp emp = (Emp)clz.getConstructor().newInstance();
Object emp = clz.getConstructor().newInstance();
Field sf = clz.getDeclaredField("salary");
sf.setAccessible(true); // 設定這個屬性不需要做安全檢查了,可以直接通路
System.out.println(sf.get(emp));
}
/**
* 構造器方法的操作
*/
public static void test05() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Emp");
CtConstructor[] cs = cc.getConstructors();
for (CtConstructor c: cs) {
System.out.println(c.getLongName());
// c.insertBefore(...);
}
}
public static void main(String[] args) throws Exception{
// test01(); // testXX 不能同時調用
// test02();
// test03();
// test04();
test05();
}
}
Output
Output
test01
test02
test03
test04
test05