Java Language Javassist Basic

Esempio

Javassist è una libreria di strumentazione bytecode che consente di modificare bytecode iniettando codice Java che verrà convertito in bytecode da Javassist e aggiunto alla classe / metodo instrument in fase di esecuzione.

Consente di scrivere il primo trasformatore che effettivamente prende una classe ipotetica "com.my.to.be.instrumented.MyClass" e aggiunge alle istruzioni di ciascun metodo una chiamata di registro.

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;
    }
}

Ora per utilizzare questo trasformatore (in modo che la nostra JVM chiamerà il metodo transform su ogni classe al momento del caricamento) dobbiamo aggiungere questo strumento o questo con un agente:

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

L'ultimo passo per iniziare il nostro primo esperimento con lo strumento è registrare effettivamente questa classe di agenti sull'esecuzione della macchina JVM. Il modo più semplice per farlo è registrarlo con un'opzione nel comando shell:

java -javaagent:myAgent.jar MyJavaApplication

Come possiamo vedere, il progetto agent / transformer viene aggiunto come jar all'esecuzione di qualsiasi applicazione denominata MyJavaApplication che deve contenere una classe denominata "com.my.to.be.instrumented.MyClass" per eseguire effettivamente il nostro codice inserito.