Dies beantwortet nicht die ursprüngliche Frage, aber da die Frage für jede ContextClassLoader
Abfrage einen hohen Rang hat und verknüpft ist , halte ich es für wichtig, die zugehörige Frage zu beantworten, wann der Kontextklassenlader verwendet werden sollte. Kurze Antwort: Verwenden Sie niemals den Kontextklassenlader ! Stellen Sie es jedoch auf ein, getClass().getClassLoader()
wenn Sie eine Methode aufrufen müssen, bei der ein ClassLoader
Parameter fehlt .
Wenn Code aus einer Klasse zum Laden einer anderen Klasse auffordert, ist der richtige Klassenlader derselbe Klassenlader wie die aufrufende Klasse (dh getClass().getClassLoader()
). Auf diese Weise funktionieren die Dinge in 99,9% der Fälle, da die JVM dies selbst tut, wenn Sie zum ersten Mal eine Instanz einer neuen Klasse erstellen, eine statische Methode aufrufen oder auf ein statisches Feld zugreifen.
Wenn Sie eine Klasse mithilfe von Reflection erstellen möchten (z. B. beim Deserialisieren oder Laden einer konfigurierbaren benannten Klasse), sollte die Bibliothek, die die Reflektion ausführt, die Anwendung immer fragen, welchen Klassenlader sie verwenden soll, indem sie den ClassLoader
als Parameter von der Anwendung empfängt . Die Anwendung (die alle Klassen kennt, die erstellt werden müssen) sollte sie bestehen getClass().getClassLoader()
.
Jede andere Möglichkeit, einen Klassenlader zu erhalten, ist falsch. Wenn eine Bibliothek verwendet Hacks wie Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
oder sun.reflect.Reflection.getCallerClass()
es ist ein durch einen Mangel in der API verursacht Fehler. Grundsätzlich Thread.getContextClassLoader()
existiert nur, weil derjenige, der die ObjectInputStream
API entworfen hat, vergessen hat, den ClassLoader
als Parameter zu akzeptieren , und dieser Fehler die Java-Community bis heute verfolgt hat.
Das heißt, viele, viele JDK-Klassen verwenden einen der wenigen Hacks, um einen zu verwendenden Klassenlader zu erraten. Einige verwenden das ContextClassLoader
(was fehlschlägt, wenn Sie verschiedene Apps in einem gemeinsam genutzten Thread-Pool ausführen oder wenn Sie das verlassen ContextClassLoader null
), andere gehen den Stapel entlang (was fehlschlägt, wenn der direkte Aufrufer der Klasse selbst eine Bibliothek ist), andere verwenden den Systemklassenlader (was in Ordnung ist, solange dokumentiert ist, dass nur Klassen im CLASSPATH
) oder Bootstrap-Klassenladeprogramm verwendet werden, und einige verwenden eine unvorhersehbare Kombination der oben genannten Techniken (was die Dinge nur verwirrender macht). Dies hat zu viel Weinen und Zähneknirschen geführt.
Versuchen Sie bei Verwendung einer solchen API zunächst, eine Überladung der Methode zu finden, die den Klassenlader als Parameter akzeptiert . Wenn es keine sinnvolle Methode gibt, versuchen Sie, die ContextClassLoader
vor dem API-Aufruf festzulegen (und danach zurückzusetzen):
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
ClassB
muss sich das auf dem Klassenpfad desClassA
Laders (oderClassA
der Eltern des Laders) befinden? Kann derClassA
Loader nicht überschrieben werdenloadClass()
, sodass er erfolgreich geladen werden kann,ClassB
auch wenn erClassB
sich nicht in seinem Klassenpfad befindet?