Verwechseln
In der Vergangenheit habe ich beim Betrachten des Quellcode den Code im Framework immer mit Thread.currentThread.getContextClassloader () begegnet, um den Kontextklassenload des aktuellen Threads zu erhalten und mit diesem Kontextklassenloader die Klasse zu laden.
Wenn wir normalerweise Code in einem Programm schreiben, verwenden wir normalerweise Class.Forname (), um die benötigten Klassen zu laden. Das häufigste ist beispielsweise, dass wir beim Programmieren von JDBC Class.Forname () zum Laden des JDBC -Treibers verwenden.
try {return class.forname ("oracle.jdbc.driver.oracledriver");} catch (classNotFoundException e) {// überspringen}Warum also versuchen wir, wenn wir Class.forname () zum Laden einer Klasse verwenden, wenn die Klasse nicht gefunden werden kann, immer noch versuchen, die Klasse mit dem von Thread.CurrentThread.getContextloader () erhaltenen Klassenloader zu laden? Zum Beispiel können wir auf den folgenden Code stoßen:
try {return class.forname (className);} catch (classNotFoundException e) {// übersprungen} classloader ctxClassloader = thread.currentThread (). getContextClassloader (); if (ctxclassloader! } catch (classNotFoundException e) {// überspringen}}Der mutige Teil hier besteht darin, den von Thread.currentThread.getContextloader () erhaltenen Loader zu verwenden, um die Klasse zu laden. Offensichtlich kann der Klassenlader, der bei class.Forname () verwendet wird, die Klasse kann sich von dem von Thread.currentThread.getContextloader () erhaltenen Klassenloader unterscheiden. Warum tritt der Unterschied auf?
Java -Klasse Loader
Bevor Sie verstehen, warum dieser Klassenlader von Thread.currentThread.getContextloader () verwendet wird, lassen Sie uns zunächst den in der JVM verwendeten Klassenloader (Classloader) verstehen.
Standardmäßig gibt es drei Arten von Klassenloadern in JVM:
Bootstrap -Klassenlader
Der Bootstrap Class Loader Class Loader ist ein in JDK eingebauter Klassenlader, mit dem Klassen in JDK geladen werden. Mit dem Bootstrap -Klassenloader werden die Klassen unter $ java_home/jre/lib im JDK geladen, wie beispielsweise die Klassen im Rt.jar -Paket. Der Bootstrap -Klassenloader ist Teil des JVM und im Allgemeinen in nativem Code geschrieben.
Verlängerungsklassenlader
Der Lader der Erweiterungsklasse Loader Class wird hauptsächlich zum Laden von Klassen im JDK -Erweiterungspaket verwendet. Im Allgemeinen werden die Pakete unter $ java_home/lib/ext über diesen Klassenlader geladen, und die Klassen unter diesem Paket beginnen im Grunde genommen mit Javax.
Systemklassenlader
Die Systemklassenlader -Klassenlader wird auch als Anwendungsklassenlader (AppClassloader) bezeichnet. Wie der Name schon sagt, wird dieser Klassenlader verwendet, um den Anwendungscode zu laden, den Entwickler normalerweise schreiben. Der Systemklassenlader wird verwendet, um im Klassenpfad gespeicherte Klassen auf Anwendungsebene zu laden.
Der folgende Code listet diese drei Klassenlader auf:
public class mellClass {public static void main (String [] args) {System.out.println (integer.class.getClassloader ()); System.out.println (logging.class.getClassloader ()); System.out.println (machclass.class.getClassloader ()); }}Wo kann man Bootstrap -Klasse -Loader für immer nullwert
null # bootstrap class lader sun.misc.launcher $EXTCLASLOADER@5e2de80c # Erweiterungsklasse Loader Sun.misc.launcher$ Applader@18b4aac2 # Systemklasse Loader
Elterndelegationsmodell
Die oben eingeführten drei Typlader sind nicht isoliert, sie haben eine hierarchische Beziehung:
Die drei Klassenlader arbeiten durch diese hierarchische Beziehung zusammen und sind für die gemeinsame Belastung von Klassen verantwortlich. Das obige hierarchische Modell wird als "Elterndelegations" -Modell des Klassenladers bezeichnet. Das übergeordnete Delegationsmodell verlangt, dass alle Klassenlader über einen übergeordneten Loader mit Ausnahme des Bootstrap-Klassenladers der obersten Ebene verfügen. Wenn der Klassenlader eine Klasse lädt, überprüfen Sie zunächst, ob Klassen im Cache geladen wurden. Wenn nicht, wird der übergeordnete Loader delegiert, um die Klasse zuerst zu laden. Der übergeordnete Loader leistet die gleiche Arbeit wie der vorherige Kinderlader, bis die Anforderung den Startstrap-Klassenlader der obersten Ebene erreicht. Wenn der übergeordnete Loader die erforderliche Klasse nicht laden kann, versucht der untergeordnete Loader, die Klasse selbst zu laden. Es funktioniert ähnlich wie folgt:
Wir können den Code in Classloader in JDK verwenden, um die Implementierung des übergeordneten Delegationsmechanismus anzuzeigen. Der Code wird in Classloader.loadClass () implementiert
Rotierte Klasse <?> LoadClass (String -Name, Boolean Resolve) löst classNotFoundException synchronisiert (GetClassLoadingLock (Name)) {// Erstens prüfen, ob die Klasse bereits geladen wurde. if (c == null) {long t0 = system.nanotime (); try {if (parent! = null) {c = parent.loadClass (Name, false); } else {c = findbootstrapCassOrnull (name); }} catch (classNotFoundException e) {// classNotFoundException, wenn die Klasse nicht gefunden wird // aus dem Nicht-Null-Elternklassen-Loader} if (c == null) {// Wenn immer noch nicht gefunden wird, rufen Sie die Fundklasse in der Reihenfolge // auf, um die Klasse zu finden. long t1 = system.nanotime (); C = FindClass (Name); // Dies ist der definierende Klassenlader; Notieren Sie die Statistiken Sun.misc.PerfCounter.getParentDelegationTime (). sun.misc.perfcounter.getFindClasstime (). AddelapsedTimeFrom (T1); sun.misc.perfcounter.getfindclasses (). Increment (); }} if (Resolve) {ResolVeclass (c); } return c; }Ein Vorteil der Verwendung der übergeordneten Delegation zur Organisation von Klassenladern besteht darin, sicher zu sein. Wenn wir selbst eine String -Klasse definieren, hoffen wir, diese String -Klasse durch die Implementierung von Java.lang.String in der Standard -Java zu ersetzen.
Wir haben die Klassendatei der String -Klasse eingestellt, die wir im Klassenpfad implementiert haben. Wenn wir den Klassenloader verwenden, um die von uns implementierte Zeichenfolge zu laden, delegiert der Klassenlader die Anforderung an den übergeordneten Loader, und lädt den Bootstrap -Klasse -Loader über Layer -für -Layer schließlich den String -Typ im RT.JAR -Paket und gibt es dann auf die ganze Art und Weise zurück. In diesem Prozess ignoriert unser Klassenlader die String -Klasse, die wir im Klassenpfad platziert haben.
Wenn der übergeordnete Delegationsmechanismus nicht übernommen wird, kann der Systemklassenlader die Stringklassendatei im ClassPath -Pfad finden und in das Programm geladen werden, wodurch die String -Implementierung im JDK überschrieben wird. Daher stellt diese Arbeitsmethode der Klassenlader sicher, dass Java -Programme in gewissem Maße sicher und stabil ausgeführt werden können.
Thread Context Class Loader
Die obigen Gespräche über so viele klassenladerbezogene Inhalte, aber immer noch nicht über das heutige Thema, Thread Context Class Loader.
Zu diesem Zeitpunkt wissen wir bereits, dass Java drei Typlader und Arbeiten gemäß einem strengen Mechanismus für Elterndelegation bietet. Auf der Oberfläche erscheint es perfekt, aber es ist dieser strenge Mechanismus für Elterndelegationsmechanismus, der beim Laden von Klassen einige Einschränkungen verursacht.
Wenn unser grundlegenderes Framework Klassen auf Anwendungsebene verwenden muss, können wir diese Klassen nur dann verwenden, wenn diese Klasse geladen wird, wenn der von unserem aktuelle Framework verwendete Klassenlader geladen werden kann. Mit anderen Worten, wir können den Klassenlader des aktuellen Klassenladers nicht verwenden. Diese Einschränkung wird durch den übergeordneten Delegationsmechanismus verursacht, da die Delegation von Klassenlastanfragen einwegs ist.
Obwohl es nicht viele Fälle gibt, gibt es immer noch eine solche Nachfrage. Ein typischer JNDI -Service. JNDI bietet eine Schnittstelle zur Abfrage von Ressourcen, die spezifische Implementierung wird jedoch von verschiedenen Herstellern implementiert. Zu diesem Zeitpunkt wird der Code von JNDI durch den Bootstrap-Klassenlader der JVM geladen. Die spezifische Implementierung ist jedoch ein anderes Code als der vom Benutzer bereitgestellte JDK, sodass er nur vom Systemklassenlader oder anderen benutzerdefinierten Klassenlader geladen werden kann. Nach dem übergeordneten Delegationsmechanismus kann JNDI die Implementierung von JNDIs SPI nicht erhalten.
Um dieses Problem zu lösen, wird ein Lader der Thread -Kontextklasse eingeführt. SetContextClassloader () des java.lang.thread class setContextClassloader () (falls nicht eingestellt wird es standardmäßig aus dem übergeordneten Thread geerbt. Wenn das Programm es nicht festgelegt hat, wird es standardmäßig an den Systemklassenlader festgelegt). Mit dem Thread Context Class Loader kann die Anwendung den von der Anwendung verwendeten Klassenlader an den Code übergeben, der den Klassenlader der obersten Ebene über java.lang.thread.setContextClassloader () verwendet. Beispielsweise kann der obige JNDI -Dienst diese Methode verwenden, um einen Klassenlader zu erhalten, der SPI -Implementierungen laden und die erforderlichen SPI -Implementierungsklassen erhalten kann.
Es ist ersichtlich, dass die Einführung von Lader der Thread -Klassen tatsächlich eine Zerstörung des übergeordneten Delegationsmechanismus ist, aber es bietet Flexibilität bei der Klassenbelastung.
Zweifel lösen
Zurück zum Anfang, um Klassen zu laden, die von Benutzern außerhalb des Frameworks implementiert sind, werden diese Klassen möglicherweise nicht über den vom Framework verwendeten Klassenlader geladen. Um das übergeordnete Delegationsmodell des Klassenloaders zu umgehen, wird Thread.getContextClassloader () verwendet, um diese Klassen zu laden.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.