Javassist Basic

Javassist 是一個位元組碼檢測庫,允許你修改注入 Java 程式碼的位元組碼,Java 程式碼將由 Javassist 轉換為位元組碼,並在執行時新增到檢測的類/方法中。

讓我們編寫實際上採用假設類“com.my.to.be.instrumented.MyClass”的第一個轉換器,並將每個方法的指令新增到日誌呼叫中。

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
 
public class DynamicTransformer implements ClassFileTransformer {
 
    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
 
        byte[] byteCode = classfileBuffer;
 
        // into the transformer will arrive every class loaded so we filter 
        // to match only what we need
        if (className.equals("com/my/to/be/instrumented/MyClass")) {
 
            try {
                // retrive default Javassist class pool
                ClassPool cp = ClassPool.getDefault();
                // get from the class pool our class with this qualified name
                CtClass cc = cp.get("com.my.to.be.instrumented.MyClass");
                // get all the methods of the retrieved class
                CtMethod[] methods = cc.getDeclaredMethods()
                for(CtMethod meth : methods) {
                    // The instrumentation code to be returned and injected
                    final StringBuffer buffer = new StringBuffer();
                    String name = meth.getName();
                    // just print into the buffer a log for example
                    buffer.append("System.out.println(\"Method " + name + " executed\" );");
                    meth.insertBefore(buffer.toString())
                }
                // create the byteclode of the class
                byteCode = cc.toBytecode();
                // remove the CtClass from the ClassPool
                cc.detach();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
 
        return byteCode;
    }
}

現在為了使用這個變換器(這樣我們的 JVM 將在載入時呼叫每個類的方法轉換),我們需要將這個工具新增到代理中:

import java.lang.instrument.Instrumentation;
 
public class EasyAgent {
 
    public static void premain(String agentArgs, Instrumentation inst) {
         
        // registers the transformer
        inst.addTransformer(new DynamicTransformer());
    }
}

開始我們的第一個儀器或實驗的最後一步是將該代理類實際註冊到 JVM 機器執行。實際執行此操作的最簡單方法是使用 shell 命令中的選項註冊它:

java -javaagent:myAgent.jar MyJavaApplication

正如我們所看到的,代理/變換器專案被新增為 jar,以執行任何名為 MyJavaApplication 的應用程式,該應用程式應包含名為“com.my.to.be.instrumented.MyClass”的類,以實際執行我們注入的程式碼。