Um ein tieferes Verständnis von Classloader zu erlangen, müssen Sie zunächst wissen, wofür Classloader verwendet wird. Wie der Name schon sagt, wird es verwendet, um Klassendateien in die JVM für die Verwendung des Programms zu laden. Wir wissen, dass Java -Programme Klassendefinitionen dynamisch laden können, und dieser dynamische Lademechanismus wird über Classloader implementiert, sodass Sie sich vorstellen können, wie wichtig Classloader ist.
Nachdem einige Freunde dies gesehen haben, denken einige Freunde möglicherweise an eine Frage, dh, da Classloader verwendet wird, um Klassen in die JVM zu laden, wie wird der Klassenloader geladen? Ist es keine Java -Klasse?
Das ist richtig, hier gibt es tatsächlich einen Klassenloader, der nicht in Java -Sprache geschrieben ist, sondern Teil der JVM -Implementierung ist. Dieser Klassenloader ist Bootstrap Classloader (Start Class Loader), dieser Klassenloader lädt die Java-Core-API, wenn die JVM ausgeführt wird, um die grundlegendsten Anforderungen des Java-Programms zu erfüllen, einschließlich des benutzerdefinierten Klassenloaders. Der sogenannte benutzerdefinierte Klassenloader hier bezieht sich auf den im Java-Programm implementierten Klassenloader. Einer ist der Extclassloader. Dieser Klassenloader wird verwendet, um die Java -Erweiterungs -API zu laden, dh der Klasse in /lib /ext, und der andere ist der AppClassloader. Dieser Klassenloader wird verwendet, um die Klasse im CLASSPATH -Einstellungsverzeichnis auf der Maschine des Benutzers zu laden. Normalerweise werden die angepassten Klassen des Programmierers vom Klassenloader geladen, ohne den Klassenloader anzugeben.
Beim Ausführen eines Programms startet das JVM den Startstrap -Klassenloader. Der Classloader lädt die Java -Core -API (Extclassloader und AppClassloader werden zu diesem Zeitpunkt ebenfalls geladen). Anschließend ruft Extclassloader auf, um die Erweiterungs -API zu laden, und lädt schließlich die im CLASSPATH -Verzeichnis definierte Klasse. Dies ist der grundlegendste Ladeprozess eines Programms.
Das obige erklärt kurz die Rolle des Klassenloaders und den grundlegendsten Ladevorgang. Als nächstes erklären wir die Art und Weise, wie Classloader lädt. Hier müssen wir über die Verwendung des übergeordneten Delegiertenmodus für das Laden der Klassen sprechen.
Jeder benutzerdefinierte Klassenloader muss den abstrakten Klassenloader erben, und jeder Klassenloader verfügt über einen übergeordneten Klassenloader. Wir können sehen, dass es im abstrakten Klassenloader eine GetParent () -Methode gibt, mit der das übergeordnete Klassenloader zurückgegeben wird. Beachten Sie, dass dieses übergeordnete übergeordnete sich nicht auf die ererbte Klasse bezieht, sondern ein Klassenloader, der beim Instanziieren des Klassenloaders angegeben ist. Wenn dieses übergeordnete übergeordnete NULL ist, ist das Standard übergeordnete übergeordnete übergeordnete übergeordnete Klassenloader der Bootstrap Classloader. Wie nutzt dieser Elternteil?
Wir können diese Situation berücksichtigen. Angenommen, wir haben einen Clientdef -Classloader angepasst und verwenden diesen benutzerdefinierten Klassenloader, um Java.lang.String zu laden, und wird dann von diesem Klassenloader geladen? Tatsächlich wird die Java.lang.String -Klasse nicht vom Clientdef -Classloader geladen, sondern vom Bootstrap -Klassenloader geladen. Warum passiert das? Tatsächlich ist dies der Grund für den übergeordneten Delegiertenmodus, denn bevor ein benutzerdefinierter Klassenloader eine Klasse lädt, delegiert er den Vaterklassenlader zunächst zum Laden. Es wird nur von selbst geladen, nachdem der Vaterklassenloader nicht erfolgreich geladen werden kann. Im obigen Beispiel delegiert der Classloader, da Java.lang.String eine Klasse zur Java -Core -API ist. Wenn der Clientdef -Classloader zum Laden verwendet wird, delegiert der Klassenloader zuerst seinen Vaterklassenloader zum Laden. Wie oben erwähnt, ist der übergeordnete Klasse von Classloader, wenn der übergeordnete Klassenloader null ist, der Bootstrap -Klassenloader. Auf der obersten Ebene des Classloaders befindet sich der Bootstrap Classloader. Daher wird der Bootstrap -Klassenloader schließlich an Bootstrap delegieren, wenn der Klassenloader vorhanden ist. Der Bootstrap -Klassenloader gibt die Streicherklasse zurück.
Schauen wir uns einen Quellcode im Classloader an:
Protected Synchronized Class LoadClass (String -Name, Boolean Resolve) löst ClassNotFoundException aus {// Überprüfen Sie zunächst, ob die mit dem Namen angegebene Klasse geladen wurde. if (c == null) {try {if (übergeordnet! } else {// übergeordnet ist null. }} catch (classNotFoundException e) {// Wenn das Laden noch nicht erfolgreich ist, rufen Sie Ihre eigene FindClass an, um c = FindClass (Name); }} if (Resolve) {ResolVeclass (c); } return c; } Aus dem obigen Code können wir sehen, dass der allgemeine Prozess des Ladens einer Klasse das gleiche ist wie das Beispiel, das ich zuvor gegeben habe. Wenn wir eine benutzerdefinierte Klasse implementieren möchten, müssen wir nur die FindClass -Methode implementieren.
Warum dieses übergeordnete Delegationsmodell verwenden?
Der erste Grund ist, dass dies wiederholtes Laden vermeiden kann. Wenn der Vater die Klasse geladen hat, muss der Unterrichtslader des Kinderklassens nicht wieder geladen werden.
Der zweite Grund ist, Sicherheitsfaktoren zu berücksichtigen . Stellen wir uns vor, wir können die definierten Typen in der Java -Core -API jederzeit dynamisch ersetzen, wenn wir diesen Delegiertenmodus nicht verwenden, was ein sehr großes Sicherheitsrisiko darstellt. Die übergeordnete Delegate-Methode kann diese Situation vermeiden, da die Zeichenfolge bereits beim Start geladen ist, sodass die benutzerdefinierte Klasse einen benutzerdefinierten Klassenloader nicht laden kann.
Das obige ist eine kurze Einführung in den Lademechanismus des Klassenloaders . Als nächstes muss ich eine andere Klasse im Zusammenhang mit Classloader erklären, dh der Klassenklasse. Jede vom Classloader geladene Klassendatei wird schließlich vom Programmierer als Instanz der Klassenklasse verwiesen. Wir können die Klassenklasse als Vorlage der gewöhnlichen Klasse behandeln. Das JVM generiert entsprechende Instanzen basierend auf dieser Vorlage und wird schließlich vom Programmierer verwendet.
Wir sehen, dass es in der Klassenklasse eine statische Methode gibt. Diese Methode entspricht der Lastklasse -Methode im Classloader. Es wird verwendet, um die Klasse zu laden, aber die beiden haben unterschiedliche Funktionen.
Klasse <?> LoadClass (String -Name)
Klasse <?> LoadClass (String -Name, boolean Resolve)
Wir sehen die beiden oben genannten Methodendeklarationen. Der zweite Parameter der zweiten Methode wird verwendet, um festzustellen, ob beim Laden der Klasse die Klasse verbindet. Wenn es zutrifft, wird es angeschlossen, sonst wird es nicht verbunden.
Apropos Verbindung, ich muss es hier erklären. Beim Laden einer Klasse durch JVM müssen drei Schritte durchgeführt werden: Laden, Verbinden und Initialisieren. Das Laden bedeutet, die entsprechende Klassendatei zu finden, sie in die JVM zu lesen und sie zu initialisieren, muss besprochen werden. Das Wichtigste ist, über die Verbindung zu sprechen.
Die Verbindung ist in drei Schritte unterteilt. Der erste Schritt besteht darin, zu überprüfen, ob die Klasse den Spezifikationen erfüllt. Der zweite Schritt ist die Vorbereitung. Es soll Speicher für die Klassenvariablen zuweisen und den Standard -Anfangswert festlegen. Der dritte Schritt ist Erklärung. Dieser Schritt ist optional. Gemäß dem zweiten Parameter der obigen Lastklasse wird festgestellt, ob eine Erklärung erforderlich ist. Die sogenannte Erklärung basiert auf der Definition des Buches "eingehender JVM", nämlich die entsprechende Entität basierend auf den Symbolreferenzen in der Klasse und dann die Symbolreferenz durch eine direkte Referenz zu ersetzen. Es ist ein bisschen tiefgreifend, haha, ich werde es hier nicht erklären. Wenn Sie mehr wissen möchten, lesen Sie bitte "eingehender JVM". Haha, wenn Sie es Schritt für Schritt weiter erklären, werden Sie nicht wissen, wann es fertig ist.
Schauen wir uns die Lastklassenmethode mit zwei Parametern an. Im Java -API -Dokument ist die Definition dieser Methode geschützt, was bedeutet, dass die Methode geschützt ist und die Methode, die Benutzer wirklich verwenden sollten, die mit einem Parameter ist. Die Lastklasse -Methode eines Parameters ruft die Methode tatsächlich mit zwei Parametern auf, und die zweite Parameter -Standardeinstellung nach False. Daher ist hier zu sehen, dass die Ladeklasse durch LoadClass beim Laden tatsächlich nicht erklärt wird, sodass die Klasse nicht initialisiert wird. Die Forname -Methode der Klassenklasse ist das Gegenteil. Beim Laden mit Forname wird die Klasse erklärt und initialisiert. Forname verfügt auch über eine andere Version der Methode, mit der festgelegt werden kann, ob der Klassenloader initialisiert und festgelegt werden soll. Ich werde hier nicht mehr darüber reden.
Ich frage mich, ob die Erklärung dieser beiden Lademethoden klar genug ist. Lassen Sie uns hier ein Beispiel geben. Zum Beispiel verwenden wir beim Laden des JDBC -Treibers den Forname anstelle der LoadClass -Methode von Classloader beim Laden von JDBC -Treibern? Wir wissen, dass sich der JDBC -Treiber über den TriverManager befindet und im TriverManager registriert sein muss. Wenn die Fahrerklasse nicht initialisiert wird, kann sie nicht im TriverManager registriert werden. Daher muss Forname anstelle einer Lastklasse verwendet werden.
Über Classloader können wir den Klassenlader anpassen und die von uns benötigte Lademethode anpassen, z. B. das Laden aus dem Netzwerk, das Laden aus anderen Dateienformaten usw. Tatsächlich gibt es immer noch viele Dinge, die in Classloader nicht erwähnt wurden, wie beispielsweise einige Implementierungen innerhalb des Classloaders usw.
In diesem Artikel hofft der Herausgeber, dass jeder den Klassenloadermechanismus verstehen wird. Vielen Dank für Ihre Unterstützung!