Ein Artikel erklärt den Klassenlademechanismus von Tomcat

Ein Artikel erklärt den Klassenlademechanismus von Tomcat

- Vorwort -

Verstehen Sie den Klassenlademechanismus von Apache Tomcat? Dieser Artikel beginnt mit den zugrunde liegenden Prinzipien und stellt ausführlich den Quellcode, die Mechanismen und die Lösungen dar, die beim Laden von Tomcat-Klassen eine Rolle spielen. So lernen Sie die Grundlagen des Ladens von Tomcat-Klassen gründlich!

- JVM-Klassenlader -

1. JVM-Klassenlader

Wenn wir über den Tomcat-Klassenlader sprechen, müssen wir kurz über den JVM-Klassenlader sprechen, wie in der folgenden Abbildung dargestellt:

  • Bootstrap ClassLoader: Wird zum Laden der von der JVM bereitgestellten grundlegenden Laufklassen verwendet, d. h. der Kernklassenbibliothek im Verzeichnis %JAVA_HOME%/jre/lib.
  • Extension ClassLoader: Extension ClassLoader, ein von Java bereitgestellter Standarderweiterungsmechanismus, wird zum Laden anderer Jar-Pakete als der Kernklassenbibliotheken verwendet. Das heißt, solange das Jar in das angegebene Erweiterungsverzeichnis (es können mehrere sein) kopiert wird, lädt die JVM es automatisch (es muss nicht über -classpath angegeben werden). Das Standarderweiterungsverzeichnis ist %JAVA_HOME% plus e/lib/ext. Ein typisches Anwendungsszenario ist, dass Java diesen Klassenlader verwendet, um Jars zu laden, die standardmäßig von der JVM bereitgestellt werden, aber nicht zur Kernklassenbibliothek gehören. Es wird nicht empfohlen, die Klassenbibliotheken, von denen die Anwendung abhängt, im Erweiterungsverzeichnis zu platzieren, da die Klassenbibliotheken in diesem Verzeichnis für alle auf der JVM basierenden ausgeführten Anwendungen sichtbar sind.
  • Anwendungs-ClassLoader: Der Anwendungs-ClassLoader wird verwendet, um das Jar-Paket in das Verzeichnis zu laden, das durch die Umgebungsvariable CLASSPATH (nicht empfohlen) oder den Laufzeitparameter -classpath angegeben wird. Der Systemklassenlader wird normalerweise zum Laden von Jar-Anwendungspaketen und deren Starteintragsklassen verwendet (die Bootstrap-Klasse von Tomcat wird vom Systemklassenlader geladen).

Das Funktionsprinzip dieser Klassenlader ist dasselbe. Der Unterschied besteht darin, dass ihre Ladepfade unterschiedlich sind, d. h. die von der Methode findClass durchsuchten Pfade sind unterschiedlich.

Der übergeordnete Delegationsmechanismus soll sicherstellen, dass eine Java-Klasse in der JVM eindeutig ist. Wenn Sie versehentlich eine Klasse mit demselben Namen wie eine JRE-Kernklasse schreiben, z. B. die Object-Klasse, kann der übergeordnete Delegationsmechanismus sicherstellen, dass die Object-Klasse in der JRE geladen wird und nicht die von Ihnen geschriebene Object-Klasse.

Dies liegt daran, dass AppClassLoader beim Laden Ihrer Object-Klasse das Laden an ExtClassLoader delegiert und ExtClassLoader wiederum an BootstrapClassLoader delegiert. BootstrapClassLoader stellt fest, dass es die Object-Klasse bereits geladen hat und kehrt direkt zurück, ohne die von Ihnen geschriebene Object-Klasse zu laden.

Bitte beachten Sie hierbei, dass die Eltern-Kind-Beziehung von Klassenladern nicht durch Vererbung realisiert wird. Beispielsweise ist AppClassLoader keine Unterklasse von ExtClassLoader, aber die übergeordnete Mitgliedsvariable von AppClassLoader zeigt auf das ExtClassLoader-Objekt. Wenn Sie den Klassenlader anpassen möchten, erben Sie nicht AppClassLoader, sondern die abstrakte Klasse ClassLoader und schreiben Sie dann die Methoden findClass und loadClass neu. Tomcat implementiert seine eigene Klassenladelogik über einen benutzerdefinierten Klassenlader. Ich weiß nicht, ob Sie bemerkt haben, dass Sie die loadClass-Methode neu schreiben müssen, wenn Sie den übergeordneten Delegierungsmechanismus unterbrechen möchten, da die Standardimplementierung von loadClass der übergeordnete Delegierungsmechanismus ist.

2. Quellcode des Klassenladers

öffentliche abstrakte Klasse ClassLoader {
  // Jeder Klassenlader hat einen übergeordneten Loader private final ClassLoader parent;
  öffentliche Klasse<?> loadClass(String name) löst ClassNotFoundException { aus
        gibt loadClass(Name, false) zurück;
    }
     geschützte Klasse<?> loadClass(String-Name, Boolesche Auflösung)
        löst ClassNotFoundException aus
    {
            // Überprüfen Sie zunächst, ob die Klasse bereits geladen wurde
            Klasse<?> c = findLoadedClass(Name);
           // Wenn nicht geladen if (c == null) {
                wenn (Elternteil != null) {
                  // Delegieren Sie das Laden zunächst an den übergeordneten Loader. Beachten Sie, dass dies ein rekursiver Aufruf ist. c = parent.loadClass(name, false);
                } anders {
                 // Wenn der übergeordnete Loader leer ist, prüfen Sie, ob der Bootstrap Loader geladen wurde c = findBootstrapClassOrNull(name);
                }
              
            // Wenn das Laden des übergeordneten Loaders fehlschlägt, rufe seine eigene findClass zum Laden auf, if (c == null) {        
                    c = Klasse finden(Name);
                }
            } 
        
            Rückkehr c;
        }
        
    }
    //Die Methode findClass im ClassLoader muss von der Unterklasse überschrieben werden. Der folgende Code ist der entsprechende Code protected Class<?> findClass(String name){
       //1. Suchen Sie gemäß dem übergebenen Klassennamen nach der Klassendatei in einem bestimmten Verzeichnis und lesen Sie die .class-Datei in den Speicher ein ...
       //2. Rufen Sie defineClass auf, um das Byte-Array in ein Class-Objekt umzuwandeln return defineClass(buf, off, len);
    }
      // Analysieren Sie das Bytecode-Array in ein Class-Objekt und implementieren Sie es mit nativen Methoden protected final Class<?> defineClass(byte[] b, int off, int len){
    
    }
    
}

Unser benutzerdefinierter Klassenlader muss die loadClass-Methode des ClassLoaders neu schreiben.

- Tomcats Klassenlademechanismus -

1. Eigenschaften des Lademechanismus

Isolierung: Web-Anwendungsbibliotheken sind voneinander isoliert, um zu verhindern, dass abhängige Bibliotheken oder Anwendungspakete sich gegenseitig beeinflussen. Stellen Sie sich vor, wir haben zwei Webanwendungen, von denen eine Spring 2.5 und die andere Spring 4.0 verwendet, und der Anwendungsserver verwendet zum Laden einen Klassenlader. Dann kann die Webanwendung aufgrund des Überschreibens des Jar-Pakets nicht erfolgreich gestartet werden.

Flexibilität: Da die Klassenlader zwischen Webanwendungen unabhängig voneinander sind, können wir nur eine Webanwendung erneut bereitstellen, und der Klassenlader der Webanwendung wird neu erstellt, ohne dass dies Auswirkungen auf andere Webanwendungen hat. Wenn Sie einen Klassenlader verwenden, ist dies offensichtlich nicht möglich, da bei nur einem Klassenlader die Abhängigkeiten zwischen den Klassen unorganisiert sind und es unmöglich ist, die Klassen einer Webanwendung vollständig zu entfernen.

Performance: Da jede Web-Anwendung über einen Klassenlader verfügt, sucht die Web-Anwendung beim Laden von Klassen nicht nach Jar-Paketen, die in anderen Web-Anwendungen enthalten sind. Die Performance ist natürlich höher, als wenn der Anwendungsserver nur einen Klassenlader hätte.

2. Tomcat-Klassenladelösung

  • Die Rollen des Bootstrap-Klassenladers und des Erweiterungs-Klassenladers bleiben unverändert;
  • Der Systemklassenlader lädt normalerweise Klassen unter CLASSPATH, aber das Tomcat-Startskript verwendet diese Variable nicht. Stattdessen lädt es die Klasse, die Tomcat startet, z. B. bootstrap.jar, die normalerweise in catalina.bat oder catalina.sh angegeben ist. Befindet sich in CATALINA_HOME/bin;
  • Der allgemeine Klassenlader lädt einige Klassen, die häufig von Tomcat und Anwendungen verwendet werden und sich in CATALINA_HOME/lib befinden, wie z. B. servlet-api.jar.
  • Catalina ClassLoader wird verwendet, um sichtbare Klassen in den Server zu laden. Auf diese Klassen kann von Anwendungen nicht zugegriffen werden.
  • SharedClassLoader wird zum Laden gemeinsam genutzter Anwendungsklassen verwendet, die nicht vom Klassenserver abhängig sind.
  • WebappClassLoader, jede Anwendung verfügt über einen einzigartigen Webapp ClassLoader, der zum Laden der Klassen unter /WEB-INF/classes und /WEB-INF/lib dieser Anwendung verwendet wird.

Tomcat 8.5 hat den strikten übergeordneten Delegierungsmechanismus standardmäßig geändert:

  • Aus dem Cache laden;
  • Wenn sich keine solche Datei im Cache befindet, wird zuerst ExtClassLoader aufgerufen, um sie zu laden. Der Erweiterungsklassenlader folgt der übergeordneten Delegierung. Er ruft Bootstrap auf, um zu prüfen, ob die entsprechende Bibliothek vorhanden ist, und greift dann auf ExtClassLoader zurück, um die Daten unter dem Erweiterungspaket zu laden.
  • Wenn nicht geladen, laden Sie es aus /WEB-INF/classes;
  • Wenn es nicht geladen ist, laden Sie es aus /WEB-INF/lib/*.jar. Wenn es nicht geladen ist, delegiert WebAppclassLoader an SharedClassLoader, SharedClassLoader delegiert an CommonClassLoader... und delegiert dann an BootstrapClassLoader. Dann sucht BootstrapClassLoader in seinem eigenen Verzeichnis nach der entsprechenden Klasse. Wenn sie gefunden wird, wird sie geladen. Wenn nicht, delegiert sie an den ExtClassLoader der nächsten Ebene. ExtClassLoader sucht dann in seinem eigenen Verzeichnis nach der Klasse. Wenn sie gefunden wird, wird sie geladen. Wenn nicht, delegiert sie an die nächste Ebene... folgen Sie dem übergeordneten Delegierungsprinzip.

3. Analysieren Sie den Ladevorgang des Anwendungsklassenladers

Der Anwendungsklassenlader ist WebappClassLoader und seine Ladeklasse befindet sich in seiner übergeordneten Klasse WebappClassLoaderBase.

  öffentliche Klasse<?> loadClass(Stringname, Boolesche Auflösung) wirft ClassNotFoundException {
        synchronisiert (getClassLoadingLock(name)) {
            wenn (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Klasse<?> clazz = null;
            // Zugriff auf gestoppten Klassenlader protokollieren
            : Überprüfen Sie den Status des Klassenladevorgangs.    
            //Laden Sie die Klasse aus dem lokalen Cache des aktuellen ClassLoaders und geben Sie sie zurück, wenn sie gefunden wurde: clazz = findLoadedClass0(name);
            if (clazz != null) {
                wenn (log.isDebugEnabled())
                    log.debug("Klasse aus Cache zurückgeben");
                wenn (auflösen)
                    Klasse auflösen(clazz);
                Rückgabeklazz;
            }
            // Wenn kein lokaler Cache vorhanden ist, rufen Sie die Methode findLoadedClass von ClassLoader auf, um zu prüfen, ob die JVM diese Klasse geladen hat. Wenn ja, kehren Sie direkt zurück.
            clazz = findeLoadedClass(name);
            if (clazz != null) {
                wenn (log.isDebugEnabled())
                    log.debug("Klasse aus Cache zurückgeben");
                wenn (auflösen)
                    Klasse auflösen(clazz);
                Rückgabeklazz;
            }
            Zeichenfolge „Ressourcenname“ = „binaryNameToPath“ (Name, „false“).
            // Zu diesem Zeitpunkt ist javaseClassLoader der Erweiterungsklassenlader, und der Erweiterungsklassenlader wird javaseClassLoader zugewiesen.
            Ich habe versucht, den ClassLoader zu starten, aber ich habe ihn nicht gestartet.
            Boolescher Wert tryLoadingFromJavaseLoader;
            versuchen {
              .....
            //Wenn es mit getResource abgerufen werden kann //Wenn es mit getResource des Erweiterungsklassenladers abgerufen werden kann, beweist dies, dass es vom Erweiterungsklassenlader geladen werden kann. Als nächstes veranlassen Sie das Laden des Erweiterungsklassenladers, wenn (tryLoadingFromJavaseLoader) {
                versuchen {
                    //Verwenden Sie zum Laden den erweiterten Klassenlader clazz = javaseLoader.loadClass(name);
                    if (clazz != null) {
                        wenn (auflösen)
                            Klasse auflösen(clazz);
                        Rückgabeklazz;
                    }
                } Fang (ClassNotFoundException e) {
                    // Ignorieren
                }
            }
            // (0.5) Berechtigung zum Zugriff auf diese Klasse bei Verwendung eines SecurityManagers
            if (securityManager != null) {
                int i = name.letzterIndexVon('.');
                wenn (i >= 0) {
                    versuchen {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } Fang (Sicherheitsausnahme se) {
                        String-Fehler = "Sicherheitsverletzung, Versuch der Verwendung von " +
                            "Eingeschränkte Klasse: " + Name;
                        log.info(Fehler, se);
                        wirf eine neue ClassNotFoundException (Fehler, se);
                    }
                }
            }
            boolean delegateLoad = Delegierter || Filter (Name, wahr);
            // (1) Delegieren an unser übergeordnetes Element, falls gewünscht
            //Wenn wahr, verwende den übergeordneten Klassenlader zum Laden, wenn (delegateLoad) {
                wenn (log.isDebugEnabled())
                    log.debug("Delegieren an übergeordneten classloader1 " + parent);
                versuchen {
                    clazz = Klasse.forName(Name, falsch, übergeordnetes Element);
                    if (clazz != null) {
                        wenn (log.isDebugEnabled())
                            log.debug("Klasse vom übergeordneten Element wird geladen");
                        wenn (auflösen)
                            Klasse auflösen(clazz);
                        Rückgabeklazz;
                    }
                } Fang (ClassNotFoundException e) {
                    // Ignorieren
                }
            }
            // (2) Suche in lokalen Repositorien
            wenn (log.isDebugEnabled())
                log.debug("Lokale Repositories durchsuchen");
            versuchen {
                // Lokal laden clazz = findClass(name);
                if (clazz != null) {
                    wenn (log.isDebugEnabled())
                        log.debug("Klasse aus lokalem Repository laden");
                    wenn (auflösen)
                        Klasse auflösen(clazz);
                    Rückgabeklazz;
                }
            } Fang (ClassNotFoundException e) {
                // Ignorieren
            }
            // (3) Bedingungslose Delegierung an übergeordnetes Element
            //Es wurde noch nicht geladen. Versuchen Sie, es mit dem übergeordneten Klassenlader erneut zu laden, if (!delegateLoad) {
                    wenn (log.isDebugEnabled())
                    log.debug("Delegieren an übergeordneten Klassenlader am Ende: " + übergeordneter Klassenlader);
                versuchen {
                    clazz = Klasse.forName(Name, falsch, übergeordnetes Element);
                    if (clazz != null) {
                        wenn (log.isDebugEnabled())
                            log.debug("Klasse vom übergeordneten Element wird geladen");
                        wenn (auflösen)
                            Klasse auflösen(clazz);
                        Rückgabeklazz;
                    }
                } Fang (ClassNotFoundException e) {
                    // Ignorieren
                }
            }
        }
        wirf eine neue ClassNotFoundException(Name);
    }

Hinweis: In der 37. Zeile des englischen Kommentars ist vermerkt, dass der Systemklassenlader erhalten wurde. Beim Debuggen stellen wir jedoch fest, dass es sich um einen Erweiterungsklassenlader handelt. In der Praxis können wir daraus schließen, dass es sich um einen Erweiterungsklassenlader handeln sollte, denn wenn die von uns geladene Klasse bereits unter dem Pfad des Erweiterungsklassenladers vorhanden ist, ist es falsch, den Systemklassenlader direkt aufzurufen. Die folgende Abbildung zeigt die Überprüfung des nach dem Debuggen erhaltenen Klassenladers.

Zusammenfassen

Tomcat bricht das Prinzip der übergeordneten Delegierung und unterbricht tatsächlich die übergeordnete Delegierung im Anwendungsklassenlader, während andere Klassenlader weiterhin der übergeordneten Delegierung folgen.

Dies ist das Ende dieses Artikels über den Tomcat-Klassenlademechanismus. Weitere Informationen zum Tomcat-Klassenlademechanismus finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Prozess des Klassenlademechanismus von Tomcat und Quellcodeanalyse

<<:  Detaillierte Erklärung der Dreieckszeichnung und clevere Anwendungsbeispiele in CSS

>>:  Das kürzeste JS, um festzustellen, ob es sich um IE6 handelt (IE-Schreibmethode)

Artikel empfehlen

Detaillierte Erklärung der markierten Union-Typen in TypeScript 2.0

Inhaltsverzeichnis Erstellen von Zahlungsmethoden...

Auszeichnungssprachen – Nochmal auflisten

Klicken Sie hier, um zum Abschnitt „HTML-Tutorial“...

So rufen Sie die Browser-Sharing-Funktion in Vue auf

Vorwort Vue (ausgesprochen /vjuː/, ähnlich wie vi...

So implementieren Sie das N-Grid-Layout in CSS

Häufige Anwendungsszenarien Die Schnittstellen ak...

Spezifische Verwendung von CSS-Inhaltsattributen

Das Inhaltsattribut wird im Allgemeinen in den Ps...

So öffnen Sie externe Netzwerkzugriffsrechte für MySQL

Wie unten dargestellt: Führen Sie hauptsächlich A...

Analyse des Prinzips der MySQL-Indexlängenbeschränkung

Dieser Artikel stellt hauptsächlich die Analyse d...

Einige Vorschläge für HTML-Anfänger und Neulinge, Experten können sie ignorieren

Gefühle: Ich bin Backend-Entwickler. Manchmal fühl...

Reacts Übergang von Klassen zu Hooks

Inhaltsverzeichnis ReagierenHooks Vorwort WarumHo...