Java字節碼修改 - javassist

AOP的實現一般使用了動態代理和字節碼修改,本文介紹使用javassist實現類的創建和修改

添加依賴

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>

使用字節碼創建一個類

  • 初始化ClassPool
ClassPool pool = ClassPool.getDefault();
  • 創建一個類TenUser,包含一個字段name,以及響應的setter和getter方法
//創建類
CtClass ctClass = pool.makeClass("com.tenmao.learn.TenUser");
//創建字段name
CtField nameField = new CtField(pool.get("java.lang.String"), "name", ctClass);
//設置爲private
nameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(nameField);

//添加getName和setName方法
ctClass.addMethod(CtNewMethod.getter("getName", nameField));
ctClass.addMethod(CtNewMethod.setter("setName", nameField));
  • 增加無參構造方法
//增加無參構造方法:其中 $0 表示 this,$1 表示參數
CtConstructor noArgsCons = new CtConstructor(new CtClass[]{}, ctClass);
noArgsCons.setBody("{$0.name=\"mark\";}");
ctClass.addConstructor(noArgsCons);
  • 增加有參構造方法
// 增加有參構造方法
CtConstructor hasArgsCons =
        new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass);
hasArgsCons.setBody("{$0.name=$1;}");
ctClass.addConstructor(hasArgsCons);
  • 創建方法printName
// 創建方法
CtMethod method = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, ctClass);
method.setBody("{System.out.println($0.name);}");
ctClass.addMethod(method);

method.insertBefore("System.out.println(\"miao~\");");
  • 生成類文件
// 生成類文件:可指定路徑,默認爲當前項目根目錄
ctClass.writeFile("target/classes");
  • 創建類實例
// 創建類實例
Object person = ctClass.toClass().newInstance();

Method printName = person.getClass().getMethod("printName");
printName.invoke(person);
  • 輸出
miao~
mark

使用字節碼修改類

  • 當前類Student
public class Student {
    public void greeting() {
        System.out.println("hello student");
    }
}
  • 找到要修改的類和方法
//找到對應的類
CtClass studentClass = pool.get("com.tenmao.learn.Student");
//找到對應的方法
CtMethod greetingMethod = studentClass.getDeclaredMethod("greeting");
  • 在方法前後添加邏輯
//修改:  在greeting之前插入邏輯
greetingMethod.insertBefore("System.out.println(\"before greeting~\");");

//修改:  在greeting之前插入邏輯
greetingMethod.insertAfter("System.out.println(\"after greeting~\");");

//輸出到類(沒有這一步的話,則修改不起作用)
studentClass.toClass();

//執行方法
Student student = new Student();
student.greeting();
  • 輸出
before greeting~
hello student
after greeting~

常見問題

  • 字節碼修改類沒有生效
    需要調用修改的類CtClass.toClass,把修改的信息寫入到類字節碼
  • attempted duplicate class definition for name: "XXX"
    因爲對應的類信息已經加載,就不能修改了,否則就是重複定義

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章