2010-04-21 11 views

risposta

27

Instrumentation.getInitiatedClasses(ClassLoader) può fare quello che vuoi.

Secondo la documentazione:

restituisce un array di tutte le classi per cui caricatore è un caricatore iniziatore.

Non sono sicuro di cosa significhi "avviare caricatore". Se questo non dà il risultato giusto prova a usare il metodo getAllLoadedClasses() e filtra manualmente con ClassLoader.


Come ottenere un'istanza di Instrumentation

Solo il JAR agente (che è separato dalla JAR dell'applicazione) può ottenere un'istanza dell'interfaccia Instrumentation. Un modo semplice per renderlo disponibile all'applicazione è creare un agent JAR contenente una classe con un metodo premain che non faccia altro che salvare un riferimento all'istanza Instrumentation nelle proprietà di sistema.

Esempio classe agente:

public class InstrumentHook { 

    public static void premain(String agentArgs, Instrumentation inst) { 
     if (agentArgs != null) { 
      System.getProperties().put(AGENT_ARGS_KEY, agentArgs); 
     } 
     System.getProperties().put(INSTRUMENTATION_KEY, inst); 
    } 

    public static Instrumentation getInstrumentation() { 
     return (Instrumentation) System.getProperties().get(INSTRUMENTATION_KEY); 
    } 

    // Needn't be a UUID - can be a String or any other object that 
    // implements equals().  
    private static final Object AGENT_ARGS_KEY = 
     UUID.fromString("887b43f3-c742-4b87-978d-70d2db74e40e"); 

    private static final Object INSTRUMENTATION_KEY = 
     UUID.fromString("214ac54a-60a5-417e-b3b8-772e80a16667"); 

} 

Esempio manifesto:

Manifest-Version: 1.0 
Premain-Class: InstrumentHook 

Il JAR risultante deve essere poi applicata dall'applicazione e specificato sulla linea di comando (con l'opzione -javaagent) quando avvio dell'applicazione. Potrebbe essere caricato due volte in diversi ClassLoader s, ma questo non è un problema poiché il sistema Properties è un singleton per processo.

classe di applicazione Esempio

public class Main { 
    public static void main(String[] args) { 
     Instrumentation inst = InstrumentHook.getInstrumentation(); 
     for (Class<?> clazz: inst.getAllLoadedClasses()) { 
      System.err.println(clazz.getName()); 
     } 
    } 
} 
+0

Ma come ottenere un'istanza Strumentazione? –

+1

@Arne Burmeister, vedere la descrizione del pacchetto: http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html – finnw

+0

quindi se riesco a visualizzarlo correttamente, devo scrivere un agente, che contiene un metodo siglato come "public static void premain (String agentArgs, Instrumentation inst);" e quindi invocare i metodi Instrumentation tramite inst ref? – Yaneeve

46

Prova questo. È una soluzione hacker ma lo farà.

Il campo classes in qualsiasi classloader (con Sun impl da 1,0) mantiene un rigido riferimento alle classi definite dal caricatore in modo che non siano GC. Puoi trarre beneficio dalla riflessione.

Field f = ClassLoader.class.getDeclaredField("classes"); 
f.setAccessible(true); 

ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
Vector<Class> classes = (Vector<Class>) f.get(classLoader); 
+0

Su Android ottengo 'Nessuna classe di campo in classe Ljava/lang/ClassLoader' –

+0

@NathanH, tecnicamente, Android non è nemmeno Java (fino a quando non ottiene OpenJDK), ma come ho menzionato è una soluzione hacker basata sul modo in cui il programma di caricamento di classe è stato implementato per oltre 17 anni. Non c'è alcuna garanzia che l'hack proposto funzionerebbe anche in futuro. Anche su Android non riesco a vedere codice in esecuzione simile al middleware (con dynamic charloading), quindi non ci dovrebbe essere bisogno di ottenere le classi caricate. – bestsss

+0

Android può eseguire il caricamento dinamico delle classi, lo faccio sempre con [JavaX] (http://javax.ai1.lol). In effetti, carico tutti i miei programmi principali in questo modo (l'app è solo uno stub). –