Suche…


Klassen mit Agenten ändern

Stellen Sie zunächst sicher, dass der verwendete Agent in der Manifest.mf die folgenden Attribute aufweist:

Can-Redefine-Classes: true
Can-Retransform-Classes: true

Beim Starten eines Java-Agenten kann der Agent auf die Klasse Instrumentation zugreifen. Mit Instrumentation können Sie addTransformer (ClassFileTransformer Transformator) aufrufen . Mit ClassFileTransformers können Sie die Bytes von Klassen neu schreiben. Die Klasse verfügt nur über eine einzige Methode, die den ClassLoader bereitstellt, der die Klasse, den Namen der Klasse, eine Instanz von java.lang.Class, die ProtectionDomain-Instanz und schließlich die Bytes der Klasse selbst lädt.

Es sieht aus wie das:

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
          ProtectionDomain protectionDomain, byte[] classfileBuffer)

Das Ändern einer Klasse nur aus Bytes kann sehr lange dauern. Um dies zu beheben, gibt es Bibliotheken, mit denen die Klassenbytes in etwas Nutzbareres umgewandelt werden können.

In diesem Beispiel verwende ich ASM, aber andere Alternativen wie Javassist und BCEL weisen ähnliche Funktionen auf.

ClassNode getNode(byte[] bytes) {
    // Create a ClassReader that will parse the byte array into a ClassNode
    ClassReader cr = new ClassReader(bytes);
    ClassNode cn = new ClassNode();
    try {
        // This populates the ClassNode
        cr.accept(cn, ClassReader.EXPAND_FRAMES);
        cr = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cn;
}

Von hier aus können Änderungen am ClassNode-Objekt vorgenommen werden. Dies macht das Ändern des Feld- / Methodenzugriffs unglaublich einfach. Mit der Tree-API von ASM ist das Ändern des Bytecodes von Methoden ein Kinderspiel.

Sobald die Änderungen abgeschlossen sind, können Sie den ClassNode mit der folgenden Methode wieder in Byte konvertieren und in der transform- Methode zurückgeben:

public static byte[] getNodeBytes(ClassNode cn, boolean useMaxs) {
    ClassWriter cw = new ClassWriter(useMaxs ? ClassWriter.COMPUTE_MAXS : ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    byte[] b = cw.toByteArray();
    return b;
}

Hinzufügen eines Agenten zur Laufzeit

Agenten können zur Laufzeit einer JVM hinzugefügt werden. Um einen Agenten zu laden, müssen Sie die VirtualMachine.attatch (String-ID) der Attach-API verwenden. Sie können dann eine kompilierte Agenten-Dose mit der folgenden Methode laden:

public static void loadAgent(String agentPath) {
    String vmName = ManagementFactory.getRuntimeMXBean().getName();
    int index = vmName.indexOf('@');
    String pid = vmName.substring(0, index);
    try {
        File agentFile = new File(agentPath);
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(agentFile.getAbsolutePath(), "");
        VirtualMachine.attach(vm.id());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Dies ruft im geladenen Agenten nicht premain ((String agentArgs, Instrumentation inst) auf, sondern stattdessen agentmain (String agentArgs, Instrumentation inst) . Dazu muss Agent-Class im Agenten Manifest.mf festgelegt werden.

Einrichten eines Basisagenten

Die Premain-Klasse enthält die Methode "premain (String agentArgs Instrumentation inst)".

Hier ist ein Beispiel:

import java.lang.instrument.Instrumentation;

public class PremainExample {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println(agentArgs);
    }
}

Wenn Sie es in eine JAR-Datei kompilieren, öffnen Sie das Manifest und stellen Sie sicher, dass es über das Attribut "Premain-Class" verfügt.

Hier ist ein Beispiel:

Premain-Class: PremainExample

Um den Agenten mit einem anderen Java-Programm "myProgram" zu verwenden, müssen Sie den Agenten in den JVM-Argumenten definieren:

java -javaagent:PremainAgent.jar -jar myProgram.jar


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow