ho un problema non sono in grado di risolvere. Supponiamo abbiamo le seguenti due classi e una relazione di ereditarietà:dinamica bytecode strumentazione - problema
public class A {
}
public class B extends A {
public void foo() {}
}
voglio strumento codice aggiuntivo in modo tale che appare come segue:
public class A {
public void print() { }
}
public class B extends A {
public void foo() { print(); }
}
Al fine di raggiungere questo obiettivo, ho basato la mia implementazione sul pacchetto java.lang.instrument
, utilizzando un agente con il mio trasformatore di file di classe. Il meccanismo è anche noto come strumentazione bytecode dinamica.
Pezzo di torta finora. Ora, il mio metodo di prova esegue le seguenti operazioni:
Codice:
B b = new B();
b.foo();
Questo non funziona a causa del seguente restrizione nel pacchetto di strumentazione: quando si chiama new B()
, la strumentazione inizia con classe B e finisce in un errore di compilazione quando si carica la classe manipolata mentre la super classe A non ha ancora il metodo print()! Si pone la questione se e come posso far scattare la strumentazione di classe A, prima di classe B. La transform() il metodo della mia classfiletransformer deve essere richiamato con classe A esplicitamente! Così ho iniziato a leggere e urtato questo:
javadoc s' The java.lang.instrument.ClassFileTransformer.transform()
dice:
Il trasformatore sarà chiamato per ogni nuova definizione di classe e ogni classe di ridefinizione. La richiesta di una definizione nuova classe è fatto con ClassLoader.defineClass. La richiesta per una ridefinizione di classe è fatto con Instrumentation.redefineClasses o suoi equivalenti nativi.
Il metodo di trasformare arriva con un'istanza di class loader, così ho pensato, perché non chiamare i loadClass
metodo (loadClass
chiamate defineClass
) me stesso con classe A quando è iniziata la strumentazione di B. Mi aspettavo che il metodo dello strumento sarà chiamato come risultato, ma purtroppo questo non era il caso. Invece la classe A
è stata caricata senza strumentazione. (L'agente non intercetta il processo di caricamento anche se dovrebbe)
Qualche idea, come risolvere questo problema? Vede un motivo per cui non è possibile che un agente che manipola qualche bytecode non possa caricare manualmente un'altra classe che, si spera, possa inviare anche attraverso quel/qualsiasi agente?
Si noti che il codice seguente funziona correttamente poiché A è stato caricato e strumentato prima che B venga manipolato.
A a = new A();
B b = new B();
b.foo();
Grazie mille!
Thx per la risposta. Vediamo il problema da una prospettiva diversa. Supponiamo che entrambe le classi, A e B, siano vuote. aggiungi un log all'inizio e alla fine del metodo di trasformazione, in modo che possiamo vedere quale classe viene caricata dall'agente e in quale momento. eseguire: new B() il risultato dovrebbe essere: quella classe B viene caricato da l'agente e poi di classe A. si può provare subito a caricare classe A manualmente utilizzando il metodo Classloader.loadClass() in ur agente quando B lo passa? il risultato è: B è stato caricato tramite l'agente, A non lo era! Giusto? Cheers christoph –
Prima di tutto, grazie mille per la risposta e lo sforzo. Lo apprezzo. U hai ragione! Ho usato Javassist per qualsiasi trasformazione. Javassist ricompila le modifiche. Ciò comporta l'errore di compilazione menzionato sopra. ASM lavora direttamente sul bytecode e non c'è bisogno di ricompilare. Riguardo al caricamento di classi all'interno di un agente: non capisco la risposta.Uso eclipse e se aggiungo il mio agente: > // se il nome della classe è B > Class.forName ("A"); e seguo l'esecuzione nel debugger, nessuna eccezione viene lanciata e l'agente non è inserito –
Ancora una volta, hai ragione. L'eccezione che hai citato è corretta. Rendiamo le cose ancora più semplici: entrambe le classi non hanno metodi e non sono pensate per essere strumentate affatto. L'unica cosa che l'agente dovrebbe fare è: se la classe B passa l'agente, Class.forName ("A"); deve essere invocato. Ciò dovrebbe attivare l'ordine corretto di caricamento della classe (prima A, poi B). Prova questo esempio. Vedrai che solo B passa l'agente! Quindi sorge la domanda perché A non passa l'agente quando viene chiamato come parte di un agente. –