Eine kurze Erläuterung, wie Tomcat den übergeordneten Delegationsmechanismus unterbricht

Eine kurze Erläuterung, wie Tomcat den übergeordneten Delegationsmechanismus unterbricht

Wir stoßen häufig auf ClassNotFound-Ausnahmen, die darauf hinweisen, dass beim Versuch, eine Klasse zu laden, ein Fehler der JVM aufgetreten ist.

Um diese Ausnahme zu lösen, müssen Sie wissen

  • Was ist Klassenladen?
  • So lädt die JVM Klassen
  • Warum tritt ClassNotFound auf?

Überlegen Sie, wie Tomcat Servlets unter Webanwendungen lädt und verwaltet?
Tomcat lädt und verwaltet Webanwendungen über die Kontextkomponente, daher werde ich heute den Klassenlademechanismus von Tomcat im Detail analysieren. Aber vorher müssen wir uns den JVM-Klassenlademechanismus ansehen. Ich werde zuerst die eingangs gestellte Frage beantworten und dann darüber sprechen, wie der Klassenlader von Tomcat den übergeordneten Delegierungsmechanismus von Java unterbricht.

JVM-Klassenlader

Beim Laden von Java-Klassen wird die Datei im Bytecode-Format .class in den Methodenbereich der JVM geladen und eine Instanz des Objekts java.lang.Class im JVM-Heap erstellt, um die mit der Java-Klasse verbundenen Daten und Methoden zu kapseln.

Was ist ein Class-Objekt?
Es kann als Vorlage der Business-Klasse verstanden werden, und die JVM erstellt basierend auf der Vorlage eine bestimmte Business-Klasse-Objektinstanz.

Die JVM lädt nicht alle .class-Dateien beim Start, sondern lädt die Klasse erst, wenn das Programm im laufenden Betrieb verwendet wird.
Das Laden von JVM-Klassen wird vom Klassenlader durchgeführt. JDK stellt eine abstrakte Klasse ClassLoader bereit:

öffentliche abstrakte Klasse ClassLoader {

    //Jeder Klassenlader hat einen übergeordneten Loader private final ClassLoader parent;
    
    öffentliche Klasse<?> loadClass(Stringname) {
  
        // Herausfinden, ob die Klasse geladen wurde Class<?> c = findLoadedClass(name);
        
        // Wenn es nicht geladen wurde if( c == null ){
          // [Rekursion] Delegieren Sie an den übergeordneten Loader zum Laden, wenn (übergeordnet != null) {
              c = übergeordnetes Element.loadClass(Name);
          } anders {
              // Wenn der übergeordnete Loader leer ist, ermitteln 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;
    }
    
    geschützte Klasse<?> findClass(Stringname){
       // 1. Suchen Sie anhand des übergebenen Klassennamens 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){
       ...
    }
}

Die Klassenlader der JVM sind hierarchische Eltern-Kind-Beziehungen und jeder Klassenlader enthält ein Elternfeld, das auf den Elternlader verweist.

  • defineClass-Toolmethode: ruft die native Methode auf, um den Bytecode der Java-Klasse in ein Class-Objekt zu analysieren
  • findClass dient zum Suchen der .class-Datei, die möglicherweise aus dem Dateisystem oder dem Netzwerk stammt. Nachdem Sie sie gefunden haben, lesen Sie die .class-Datei in den Speicher, um das Bytecode-Array abzurufen, und rufen Sie dann die Methode defineClass auf, um das Class-Objekt abzurufen

loadClass prüft zunächst, ob die Klasse geladen wurde. Wenn ja, wird sie direkt zurückgegeben, andernfalls wird sie zum Laden an den übergeordneten Loader übergeben.
Dies ist ein rekursiver Aufruf, d. h. der untergeordnete Loader hält einen Verweis auf den übergeordneten Loader. Wenn ein Klassenloader eine Java-Klasse laden muss, delegiert er das Laden zunächst an den übergeordneten Loader, und dann sucht der übergeordnete Loader in seinem eigenen Ladepfad nach der Java-Klasse. Wenn der übergeordnete Loader sie in seinem eigenen Ladebereich nicht finden kann, gibt er sie zum Laden an den untergeordneten Loader zurück. Dies ist der übergeordnete Delegationsmechanismus.

Das Funktionsprinzip des JDK-Klassenladers ist dasselbe. Der einzige Unterschied besteht im Ladepfad, d. h. der von findClass gesuchte Pfad ist anders.
Der übergeordnete Delegierungsmechanismus soll die Eindeutigkeit einer Java-Klasse in der JVM sicherstellen. Wenn Sie versehentlich eine Klasse mit demselben Namen wie eine JRE-Kernklasse schreiben, z. B. „Object“, kann der übergeordnete Delegierungsmechanismus sicherstellen, dass die Object-Klasse in der JRE geladen wird und nicht das von Ihnen geschriebene Object.
Denn wenn AppClassLoader Ihre Object-Klasse lädt, delegiert es das Laden an ExtClassLoader, und ExtClassLoader delegiert das Laden an BootstrapClassLoader. BootstrapClassLoader stellt fest, dass es die Object-Klasse bereits geladen hat, und kehrt direkt zurück, ohne Ihre Object-Klasse zu laden.

Die Eltern-Kind-Beziehung von Klassenladern wird nicht durch Vererbung implementiert. Beispielsweise ist AppClassLoader keine Unterklasse von ExtClassLoader, aber das übergeordnete Objekt von AppClassLoader zeigt auf das ExtClassLoader-Objekt.
Wenn Sie daher den Klassenlader anpassen, können Sie statt AppClassLoader die abstrakte Klasse ClassLoader erben und dann findClass und loadClass neu schreiben.
Tomcat implementiert sein eigenes Klassenladen durch einen benutzerdefinierten Klassenlader.
Wenn Sie die übergeordnete Delegierung unterbrechen möchten, müssen Sie nur loadClass neu schreiben, da die Standardimplementierung von loadClass der übergeordnete Delegierungsmechanismus ist.

Tomcat-Klassenlader

Tomcats benutzerdefinierter Klassenlader „WebAppClassLoader“ unterbricht den übergeordneten Delegierungsmechanismus:
Versuchen Sie zunächst, eine Klasse selbst zu laden. Wenn diese nicht gefunden werden kann, delegieren Sie sie an den übergeordneten Klassenlader. Ziel ist es, das Laden von Klassen zu priorisieren, die von der Webanwendung selbst definiert werden.
Schreiben Sie einfach zwei Methoden von ClassLoader neu:

Klasse finden

öffentliche Klasse<?> findClass(String name) löst ClassNotFoundException { aus
    ...
    
    Klasse<?> clazz = null;
    versuchen {
            //1. Suchen Sie zuerst im Web-Anwendungsverzeichnis nach der Klasse clazz = findClassInternal(name);
    } Fang (RuntimeException e) {
           werfen e;
       }
    
    wenn (clazz == null) {
    versuchen {
            //2. Wenn im lokalen Verzeichnis nicht gefunden, lassen Sie den übergeordneten Loader suchen clazz = super.findClass(name);
    } Fang (RuntimeException e) {
           werfen e;
       }
    
    //3. Wenn die übergeordnete Klasse nicht gefunden wird, werfen Sie ClassNotFoundException
    wenn (clazz == null) {
        wirf eine neue ClassNotFoundException(Name);
     }

    Rückgabeklazz;
}

Workflow

  • Suchen Sie zunächst im lokalen Verzeichnis der Webanwendung nach der zu ladenden Klasse
  • Wenn es nicht gefunden wird, wird es an den übergeordneten Loader, d. h. AppClassLoader, übergeben
  • Wenn der übergeordnete Loader die Klasse ebenfalls nicht findet, werfen Sie „ClassNotFound“

Ladeklasse

öffentliche Klasse<?> loadClass(Stringname, Boolesche Auflösung) wirft ClassNotFoundException {

    synchronisiert (getClassLoadingLock(name)) {
 
        Klasse<?> clazz = null;

        //1. Prüfen Sie zunächst im lokalen Cache, ob die Klasse geladen wurde. clazz = findLoadedClass0(name);
        if (clazz != null) {
            wenn (auflösen)
                Klasse auflösen(clazz);
            Rückgabeklazz;
        }

        //2. Prüfen Sie, ob der Systemklassenlader clazz = findLoadedClass(name); geladen hat.
        if (clazz != null) {
            wenn (auflösen)
                Klasse auflösen(clazz);
            Rückgabeklazz;
        }

        // 3. Versuchen Sie, die Klasse mit dem Klassenlader ExtClassLoader zu laden. Warum?
        Ich habe versucht, den ClassLoader zu starten, aber ich habe ihn nicht gestartet.
        versuchen {
            clazz = javaseLoader.loadClass(name);
            if (clazz != null) {
                wenn (auflösen)
                    Klasse auflösen(clazz);
                Rückgabeklazz;
            }
        } Fang (ClassNotFoundException e) {
            // Ignorieren
        }

        // 4. Versuche die Klasse im lokalen Verzeichnis zu suchen und zu laden try {
            clazz = Klasse finden(Name);
            if (clazz != null) {
                wenn (auflösen)
                    Klasse auflösen(clazz);
                Rückgabeklazz;
            }
        } Fang (ClassNotFoundException e) {
            // Ignorieren
        }

        // 5. Versuchen Sie, den Systemklassenlader (d. h. AppClassLoader) zum Laden zu verwenden try {
                clazz = Klasse.forName(Name, falsch, übergeordnetes Element);
                if (clazz != null) {
                    wenn (auflösen)
                        Klasse auflösen(clazz);
                    Rückgabeklazz;
                }
            } Fang (ClassNotFoundException e) {
                // Ignorieren
            }
       }
    
    //6. Das Laden der oben genannten Prozesse schlägt fehl und es wird eine Ausnahme ausgelöst: throw new ClassNotFoundException(name);
}

Workflow

  • Überprüfen Sie zunächst den lokalen Cache, um festzustellen, ob die Klasse geladen wurde
  • Das heißt, ob der Klassenlader von Tomcat diese Klasse bereits geladen hat.
  • Wenn der Tomcat-Klassenlader die Klasse nicht geladen hat, überprüfen Sie, ob der Systemklassenlader sie geladen hat.
  • Wenn keine davon vorhanden ist, lassen Sie ExtClassLoader sie laden, um zu verhindern, dass die eigenen Klassen der Webanwendung die JRE-Kernklassen überschreiben.
  • Da Tomcat die übergeordnete Delegierung unterbrechen muss, wird, wenn eine Klasse namens Object in der Webanwendung angepasst wird, die JRE-Object-Klasse überschrieben, wenn die Object-Klasse zuerst geladen wird. Daher versucht der Tomcat-Klassenlader, sie zuerst mit ExtClassLoader zu laden, da ExtClassLoader das Laden an BootstrapClassLoader delegiert. BootstrapClassLoader stellt fest, dass er die Object-Klasse bereits geladen hat, und gibt sie direkt an den Tomcat-Klassenlader zurück. Auf diese Weise lädt der Tomcat-Klassenlader die Object-Klasse nicht unter der Webanwendung und vermeidet so das Überschreiben der JRE-Kernklasse.
  • Wenn das Laden von ExtClassLoader fehlschlägt, d. h. JRE verfügt nicht über diese Klasse, suchen und laden Sie sie im lokalen Web-Anwendungsverzeichnis.
  • Wenn im lokalen Verzeichnis keine solche Klasse vorhanden ist, bedeutet dies, dass es sich nicht um eine von der Webanwendung selbst definierte Klasse handelt und sie vom Systemklassenlader geladen wird. Bitte beachten Sie hierbei, dass die Übergabe der Web-Anwendung an den Systemklassenlader durch den Aufruf von Class.forName erfolgt, da der Standardlader von Class.forName der Systemklassenlader ist.
  • Wenn der obige Ladevorgang fehlschlägt, werfen Sie ClassNotFound

Es ist ersichtlich, dass der Tomcat-Klassenlader die übergeordnete Delegierung unterbricht und zu Beginn nicht direkt an den übergeordneten Lader delegiert, sondern ihn zuerst in das lokale Verzeichnis lädt.
Um jedoch zu verhindern, dass lokale Verzeichnisklassen die JRE-Kernklassen überschreiben, werden diese zuerst mit ExtClassLoader geladen.
Warum also nicht zuerst mit AppClassLoader laden?
Wenn dies der Fall ist, wird es wieder zur übergeordneten Delegierung, was das Geheimnis des Tomcat-Klassenladers ist.

Dies ist das Ende dieses Artikels darüber, wie Tomcat den übergeordneten Delegationsmechanismus unterbricht. Weitere Informationen zum übergeordneten Delegationsmechanismus von Tomcat 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:
  • Lösung für die hohe CPU-Auslastung des Tomcat-Prozesses
  • SpringBoot startet eingebettete Tomcat-Implementierungsschritte
  • Tomcat unterbricht den übergeordneten Delegierungsmechanismus, um eine Isolierung von Webanwendungen zu erreichen
  • Verwenden Sie Tomcat, um die gemeinsam genutzte Bibliothek so einzurichten, dass sie dasselbe JAR teilt.
  • Fünfzehn Tomcat-Interviewfragen, eine seltene Gelegenheit!

<<:  Tatsächlicher Datensatz zur Wiederherstellung der MySQL-Datenbank nach Zeitpunkt

>>:  Lösungen für Unterschiede in der Browserinterpretation hinsichtlich Größe, Breite und Höhe in CSS

Artikel empfehlen

So installieren und konfigurieren Sie Redis in CentOS7

Einführung Es ist nicht nötig, Redis im Detail vo...

Beispiel für eine MySQL-DML-Sprachoperation

Zusätzliche Erklärung, Fremdschlüssel: Verwenden ...

Tabellenbezogene Anordnung und Javascript-Operationen table, tr, td

Gut funktionierende Einstellungen für Tabelleneige...

Vue implementiert Beispielcode zur Formulardatenvalidierung

Fügen Sie dem el-form-Formular Regeln hinzu: Defi...

MySQL-Abfragedaten stündlich, geben Sie 0 ein, wenn keine Daten vorhanden sind

Nachfragehintergrund Als statistische Schnittstel...

Vue implementiert die Anmeldung per Mobiltelefon-Bestätigungscode

In diesem Artikel wird der spezifische Code von V...

jquery+springboot realisiert die Datei-Upload-Funktion

In diesem Artikelbeispiel wird der spezifische Co...

Implementierung einer kreisförmigen CSS-Aushöhlung (Gutschein-Hintergrundbild)

In diesem Artikel werden hauptsächlich kreisförmi...

vue-table implementiert das Hinzufügen und Löschen

In diesem Artikelbeispiel wird der spezifische Co...

Verwendung von relativen und absoluten Pfaden unter Linux

01. Übersicht Absolute und relative Pfade kommen ...