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”的类,以实际执行我们注入的代码。