Sử dụng Javassist để Thao tác Bytecode Java

Đọc và Xuất Bytecode


ClassPool classPool = ClassPool.getDefault();
CtClass targetClass = classPool.get("example.Shape");
targetClass.setSuperclass(classPool.get("example.Point"));
targetClass.writeFile("d://output");
// byte[] bytes = targetClass.toBytecode();
// Class loadedClass = targetClass.toClass();

ClassPool quản lý việc tải lớp, hỗ trợ ba cách xuất bytecode.

Tạo Lớp Mới


ClassPool pool = ClassPool.getDefault();
CtClass newClass = pool.makeClass("Coordinate");
newClass.addMethod(method);
newClass.addField(field);

CtClass cung cấp API đơn giản để chỉnh sửa lớp.

Đóng Băng Lớp

Sau khi gọi writeFile(), toClass(), hoặc toBytecode(), CtClass bị đóng băng. Để tiếp tục chỉnh sửa:


CtClass cls = ...;
cls.writeFile();
cls.defrost();
cls.setSuperclass(...);

Khi ClassPool.doPruning=true, bộ nhớ sẽ được giải phóng khi lớp đóng băng.

Đường dẫn Tìm kiếm Lớp


ClassPool pool = ClassPool.getDefault();
pool.insertClassPath("/custom/classpath");
ClassPath urlPath = new URLClassPath("example.org", 443, "/lib/", "com.example.");
pool.insertClassPath(urlPath);
byte[] classData = ...;
String className = "Demo";
pool.insertClassPath(new ByteArrayClassPath(className, classData));
InputStream input = ...;
CtClass ct = pool.makeClass(input);

Quản lý ClassPool

Giảm Tiêu thụ Bộ nhớ


CtClass instance = ...;
instance.writeFile();
instance.detach();

Hoặc tạo ClassPool riêng:


ClassPool customPool = new ClassPool(true);
customPool.appendClassPath("./lib");

ClassPool Phân cấp


ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.insertClassPath("./modules");

Đổi Tên Lớp


ClassPool pool = ClassPool.getDefault();
CtClass original = pool.get("Vector");
original.setName("Matrix");
CtClass reloaded = pool.get("Vector");

Với lớp đã đóng băng:


CtClass frozen = pool.get("Array");
frozen.writeFile();
CtClass renamed = pool.getAndRename("Array", "Collection");

Quản lý Class Loader


ClassPool cp = ClassPool.getDefault();
CtClass hello = cp.get("Hello");
Class c = hello.toClass();

Tránh tải lại lớp đã load:


ClassPool cp = ClassPool.getDefault();
CtClass hello2 = cp.get("Hello2");
Class c2 = hello2.toClass(new CustomClassLoader());

Sử dụng Javassist Loader


ClassPool pool = ClassPool.getDefault();
Loader loader = new Loader(pool);
CtClass rectangle = pool.get("geometry.Rectangle");
rectangle.setSuperclass(pool.get("geometry.Point"));
Class cls = loader.loadClass("geometry.Rectangle");
Object obj = cls.newInstance();

Theo dõi Loader


public class CustomTranslator implements Translator {
    public void start(ClassPool pool) {}
    public void onLoad(ClassPool pool, String name) {
        CtClass cc = pool.get(name);
        cc.setModifiers(Modifier.PUBLIC);
    }
}

Sửa đổi Hệ thống Lớp


ClassPool pool = ClassPool.getDefault();
CtClass stringClass = pool.get("java.lang.String");
CtField newField = new CtField(CtClass.intType, "hidden", stringClass);
newField.setModifiers(Modifier.PUBLIC);
stringClass.addField(newField);
stringClass.writeFile(".");

Chạy với: java -Xbootclasspath/p:. MainApp

Thao tác Phương thức

Chèn Mã vào Phương thức


CtMethod method = cc.getDeclaredMethod("move");
method.insertBefore("{ System.out.println($1); System.out.println($2); }");

Biến Đặc biệt trong Phương thức

  • $0: this
  • $1, $2: tham số
  • $args: mảng tham số
  • $_: giá trị trả về
  • $r: kiểu trả về

Xử lý Ngoại lệ


CtMethod m = ...;
CtClass exType = ClassPool.getDefault().get("java.io.IOException");
m.addCatch("{ System.out.println($e); throw $e; }", exType);

Tạo Phương thức và Trường


CtClass point = ClassPool.getDefault().get("Point");
CtMethod newMethod = CtNewMethod.make(
    "public int shiftX(int delta) { x += delta; return x; }", point);
point.addMethod(newMethod);

Tạo Trường


CtClass point = ClassPool.getDefault().get("Point");
CtField field = CtField.make("public int weight = 0;", point);
point.addField(field);

Ví dụ Hoàn chỉnh


package demo.javassist;

import javassist.*;

public class ClassCreator {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass userClass = pool.makeClass("model.User");
        
        CtField idField = CtField.make("private int userId;", userClass);
        CtField nameField = CtField.make("private String userName;", userClass);
        userClass.addField(idField);
        userClass.addField(nameField);
        
        CtMethod getName = CtMethod.make("public String getUserName(){return userName;}", userClass);
        CtMethod setName = CtMethod.make("public void setUserName(String name){userName = name;}", userClass);
        userClass.addMethod(getName);
        userClass.addMethod(setName);
        
        CtConstructor paramCons = new CtConstructor(
            new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, userClass);
        paramCons.setBody("{this.userId = $1; this.userName = $2;}");
        userClass.addConstructor(paramCons);
        
        CtConstructor defaultCons = new CtConstructor(null, userClass);
        defaultCons.setBody("{}");
        userClass.addConstructor(defaultCons);
        
        userClass.writeFile("./generated");
    }
}

Thẻ: javassist bytecode manipulation Java Reflection dynamic class generation class instrumentation

Đăng vào ngày 14 tháng 8 lúc 07:43