Unterschied zwischen dem Kontextklassenlader des Threads und dem normalen classnlader

Was ist der Unterschied zwischen dem Kontextklassenlader eines Threads und einem normalen Classloader?

Das heißt, wenn Thread.currentThread().getContextClassLoader() und getClass().getClassLoader() verschiedene classnladeobjekte zurückgeben, welches wird verwendet?

Jede class verwendet einen eigenen classnlader, um andere classn zu laden. Wenn also ClassA.class auf ClassA.class verweist, muss ClassB.class auf dem classnpfad des Classloaders von ClassA oder seinen Eltern sein.

Der Threadkontextklassenlader ist der aktuelle classnlader für den aktuellen Thread. Ein Objekt kann aus einer class in ClassLoaderC und dann an einen Thread übergeben werden, der ClassLoaderD . In diesem Fall muss das Objekt Thread.currentThread().getContextClassLoader() direkt verwenden, wenn es Ressourcen laden möchte, die auf seinem eigenen classnlader nicht verfügbar sind.

Es gibt einen Artikel auf javaworld.com, der den Unterschied erklärt => Welchen ClassLoader sollten Sie verwenden?

(1)

Threadkontext-classnlader bieten eine Hintertür um das Schema zum Delegieren von classnladungen.

Nehmen wir zum Beispiel JNDI: Seine Eingeweide werden durch Bootstrap-classn in rt.jar (beginnend mit J2SE 1.3) implementiert, aber diese JNDI-coreklassen können JNDI-Provider laden, die von unabhängigen Anbietern implementiert und möglicherweise in dem classnpfad der Anwendung bereitgestellt werden. Dieses Szenario erfordert, dass ein Eltern-classnlader (in diesem Fall der Ur-classnlader) eine class lädt, die für einen seiner Kind-classnlader sichtbar ist (zum Beispiel der System-classnlader). Die normale J2SE-Delegierung funktioniert nicht, und die Problemumgehung besteht darin, die core-JNDI-classn dazu zu bringen, Thread-Kontext-Loader zu verwenden und so effektiv durch die Classloader-Hierarchie in die der eigentlichen Delegierung entgegengesetzte Richtung zu “tunneln”.

(2) aus derselben Quelle:

Diese Verwirrung wird wahrscheinlich noch einige Zeit bei Java bleiben. Nehmen Sie eine beliebige J2SE-API mit dynamischem Ressourcenladen jeglicher Art und versuchen Sie herauszufinden, welche Lade-Strategie verwendet wird. Hier ist eine Auswahl:

  • JNDI verwendet Kontextklassenlader
  • Class.getResource () und Class.forName () verwenden den aktuellen Classloader
  • JAXP verwendet Kontext-classnlader (ab J2SE 1.4)
  • java.util.ResourceBundle verwendet den aktuellen Classloader des Aufrufers
  • URL-Protokoll-Handler, die über die java.protocol.handler.pkgs-Systemeigenschaft angegeben werden, werden nur in den Bootstrap- und System-Classloadern nachgeschlagen
  • Die Java-Serialisierungs-API verwendet standardmäßig den aktuellen Classloader des Aufrufers

Dies beantwortet nicht die ursprüngliche Frage, aber da die Frage für jede ContextClassLoader Abfrage hochrangig und verknüpft ist, denke ich, dass es wichtig ist, die verwandte Frage zu beantworten, wann der ContextClassLoader verwendet werden sollte. Kurze Antwort: Verwenden Sie niemals den Context Class Loader ! Setzen Sie es jedoch auf getClass().getClassLoader() wenn Sie eine Methode aufrufen müssen, der ein ClassLoader Parameter fehlt.

Wenn Code von einer class fragt, eine andere class zu laden, ist der richtige classnlader derselbe classnlader wie die Aufruferklasse (dh getClass().getClassLoader() ). Dies ist die Art und Weise, wie die Dinge 99,9% der Zeit arbeiten, da dies die JVM selbst tut, wenn Sie zum ersten Mal eine Instanz einer neuen class konstruieren, eine statische Methode aufrufen oder auf ein statisches Feld zugreifen.

Wenn Sie eine class mithilfe von Reflektion erstellen möchten (z. B. beim Deserialisieren oder Laden einer konfigurierbaren benannten class), sollte die Bibliothek, die die Reflektion durchführt, immer die Anwendung fragen, welche classnladevorrichtung verwendet werden soll, indem sie den ClassLoader als Parameter von der Anwendung empfängt. Die Anwendung (die alle classn kennt, die konstruiert werden müssen) sollte getClass().getClassLoader() .

Jede andere Möglichkeit, einen classnlader zu erhalten, ist falsch. Wenn eine Bibliothek Hacks wie Thread.getContextClassLoader() , sun.misc.VM.latestUserDefinedLoader() oder sun.reflect.Reflection.getCallerClass() es sich um einen Fehler, der durch einen Mangel in der API verursacht wird. Grundsätzlich existiert Thread.getContextClassLoader() nur, weil derjenige, der die ObjectInputStream API entworfen hat, vergessen hat, den ClassLoader als Parameter zu akzeptieren, und dieser Fehler hat die Java-Community bis heute heimgesucht.

Das heißt, viele viele JDK-classn verwenden einen der wenigen Hacks, um einen classnlader zu erraten. Einige verwenden den ContextClassLoader (was fehlschlägt, wenn Sie verschiedene Anwendungen in einem freigegebenen ContextClassLoader null , oder wenn Sie den ContextClassLoader null ), einige gehen den Stapel (was fehlschlägt, wenn der direkte Aufrufer der class selbst eine Bibliothek ist) CLASSPATH (was gut ist, solange es dokumentiert ist, nur classn im CLASSPATH ) oder Bootstrap-classnlader, und einige verwenden eine unvorhersehbare Kombination der obigen Techniken (was die Dinge nur verwirrender macht). Dies hat zu viel Weinen und Zähneknirschen geführt.

Wenn Sie eine solche API verwenden, versuchen Sie zunächst, eine Überladung der Methode zu finden, die den classnlader als Parameter akzeptiert . Wenn es keine vernünftige Methode gibt, versuchen Sie, den ContextClassLoader vor dem API-Aufruf zu setzen (und ihn 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); } 

Hinzufügen zu @David Roussel Antwort, classn können von mehreren classnladeprogrammen geladen werden.

Lasst uns verstehen, wie classnlader funktioniert.

Von javin paul blog in javarevisited:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

ClassLoader folgt drei Prinzipien.

Delegationsprinzip

Eine class wird in Java geladen, wenn sie benötigt wird. Angenommen, Sie haben eine anwendungsspezifische class mit dem Namen Abc.class. Die erste Anfrage zum Laden dieser class wird an Application ClassLoader gesendet, die an ihre Eltern-Extension ClassLoader delegiert, die weiter zum Primordial- oder Bootstrap-classnlader delegiert

  • Bootstrap ClassLoader ist verantwortlich für das Laden von Standard-JDK-classndateien aus rt.jar und ist das übergeordnete Element aller classnladeprogramme in Java. Bootstrap Class Loader hat keine Eltern.

  • Extension ClassLoader delegiert die Anforderung zum Laden der class an das übergeordnete Bootstrap und, falls dies nicht erfolgreich ist, lädt sie das Verzeichnis class jre / lib / ext oder jedes andere Verzeichnis, auf das die Systemeigenschaft java.ext.dirs zeigt

  • System- oder Anwendungsklassenlader und ist verantwortlich für das Laden anwendungsspezifischer classn aus der CLASSPATH-Umgebungsvariablen, der Befehlszeilenoption -classpath oder -cp, dem Attribut ‘classnpfad’ der Manifestdatei in JAR.

  • Der Application Class Loader ist ein Kind des Extension ClassLoader und wird von der class sun.misc.Launcher$AppClassLoader implementiert.

HINWEIS: Mit Ausnahme des Bootstrap-classnladeprogramms , das in der Muttersprache hauptsächlich in C implementiert ist, werden alle Java-classnladeprogramme mit java.lang.ClassLoader implementiert.

Sichtbarkeitsprinzip

Gemäß dem Sichtbarkeitsprinzip kann Child ClassLoader die class sehen, die von Parent ClassLoader geladen wurde, aber umgekehrt ist nicht wahr.

Eindeutigkeitsprinzip

Nach diesem Prinzip sollte eine von Parent geladene class nicht erneut von Child ClassLoader geladen werden