Ich habe ein Problem, das ich nicht lösen kann. Nehmen wir an, wir die folgenden zwei Klassen und eine Vererbungsbeziehung haben:Dynamische Bytecode-Instrumentierung - Problem
public class A {
}
public class B extends A {
public void foo() {}
}
I-Code zusätzliches Instrument wollen, so dass es wie folgt aussieht:
public class A {
public void print() { }
}
public class B extends A {
public void foo() { print(); }
}
Um dieses Ziel zu erreichen, basierend ich meine Implementierung auf dem java.lang.instrument
Paket, mit einem Agenten mit meiner eigenen Klassendatei Transformator. Der Mechanismus wird auch als dynamische Bytecode-Instrumentierung bezeichnet.
Stück Kuchen so weit. Nun, meine Testmethode macht folgender:
Code:
B b = new B();
b.foo();
Dies nicht wegen der folgenden Einschränkung funktioniert in der Instrumentierung Paket: wenn new B()
Aufruf, die Instrumentierung beginnt mit der Klasse B und enden in einem Kompilierungsfehler beim Laden der manipulierten Klasse, da die Superklasse A noch keine print() -Methode hat! Die Frage stellt sich, ob und wie ich die Instrumentierung der Klasse A vor Klasse B auslösen kann. Die transform() - Methode meines classfiletransformers sollte explizit mit der Klasse A aufgerufen werden! Also begann ich zu lesen und stieß auf diese:
Die java.lang.instrument.ClassFileTransformer.transform()
‚s javadoc sagt:
Der Transformator für jede neue Klassendefinition und jede Klasse Neudefinition genannt wird. Die Anforderung für eine neue Klassendefinition erfolgt mit ClassLoader.defineClass. Die Anforderung für eine Klassenredefinition wird mit Instrumentation.redefineClasses oder seinen nativen Entsprechungen gestellt.
Das Transformationsverfahren zusammen mit einer Class-Loader-Instanz kommt, also dachte ich, warum nicht die loadClass
Methode (loadClass
Anrufe defineClass
) rief mich mit Klasse A, wenn die Instrumentierung von B begonnen hat. Ich habe erwartet, dass die Instrument-Methode als Ergebnis aufgerufen wird, aber das war leider nicht der Fall. Stattdessen wurde die Klasse A
ohne Instrumentierung geladen. (Der Agent fängt den Ladevorgang nicht ab, obwohl er es soll)
Irgendwelche Ideen, wie man dieses Problem löst? Sehen Sie einen Grund, warum es nicht möglich ist, dass ein Agent, der einen Bytecode manipuliert, nicht manuell eine andere Klasse laden kann, die dann hoffentlich auch über diesen/irgendeinen Agenten gesendet wird?
Beachten Sie, dass der folgende Code ordnungsgemäß funktioniert, da A geladen und instrumentiert wurde, bevor B manipuliert wird.
A a = new A();
B b = new B();
b.foo();
Vielen Dank!
Thx für deine Antwort. Lassen Sie uns das Thema aus einer anderen Perspektive betrachten. Angenommen, beide Klassen A und B sind leer. Fügen Sie am Anfang und am Ende der Transformationsmethode ein Protokoll hinzu, damit wir sehen können, welche Klasse zu welchem Zeitpunkt vom Agenten geladen wird. ausführen: new B() das Ergebnis sollte sein: die Klasse B wird vom Agenten geladen und anschließend Klasse A. können Sie jetzt versuchen Klasse A zu laden manuell die Classloader.loadClass() -Methode in ur Mittel verwendet wird wenn B es passiert? das Ergebnis ist: B wurde durch den Agenten geladen, A war nicht! Richtig? Cheers christoph –
Zunächst einmal vielen Dank für die Antwort und die Mühe. Ich weis das zu schätzen. U sind richtig! Ich habe Javassist für alle Transformationen verwendet. Javassist kompiliert die Änderungen neu. Dies führt zu dem oben erwähnten Kompilierungsfehler. ASM arbeitet direkt am Bytecode und es ist keine Neukompilierung erforderlich. Zum Laden von Klassen innerhalb eines Agenten: Ich verstehe Ihre Antwort nicht.Ich benutze Eclipse und wenn ich in meinem Agenten hinzufügen: > // wenn Klassenname ist B > Class.forName ("A"); und ich folge der Ausführung im Debugger, keine Ausnahme wird geworfen und der Agent wird nicht eingegeben –
Wieder haben Sie Recht. Die von Ihnen erwähnte Ausnahme ist korrekt. Lassen Sie uns die Dinge noch einfacher machen: Beide Klassen haben keine Methoden und sollen überhaupt nicht instrumentiert werden. Das einzige, was der Agent tun sollte, ist: wenn Klasse B den Agenten übergibt, Class.forName ("A"); soll aufgerufen werden. Dies sollte die richtige Reihenfolge des Klassenladens auslösen (A zuerst, dann B). Probieren Sie dieses Beispiel. Du wirst sehen, dass nur B den Agenten passiert! Daher stellt sich die Frage, warum A den Agenten nicht weitergibt, wenn er als Teil eines Agenten aufgerufen wird. –