Tomcat definiert intern mehrere ClassLoader, sodass Anwendungen und Container auf Klassen und Ressourcen in verschiedenen Repositories zugreifen können und gleichzeitig eine Klassenisolation zwischen den Anwendungen erreicht wird. 1. Java-Klassenlademechanismus Beim Laden von Klassen werden die kompilierten Klassendateien in den JVM-Speicher geladen (permanente Generierung/Metaspace). Der Grund, warum Klassenlader eine Klassenisolation erreichen können, besteht darin, dass zwei Klassen nur dann gleich sind, wenn sie vom selben Klassenlader geladen werden, andernfalls müssen sie ungleich sein. Beim Laden verwendet die JVM einen übergeordneten Delegationsmechanismus. Wenn der Klassenlader eine Klasse laden möchte, lautet die Ladereihenfolge: Zunächst wird die Anforderung an den übergeordneten Loader delegiert. Wenn der übergeordnete Loader die zu ladende Klasse nicht finden kann, sucht er in seinem eigenen Repository nach dem Laden. Der Vorteil dieses Mechanismus besteht darin, dass sichergestellt werden kann, dass die Kernklassenbibliothek nicht überschrieben wird. Gemäß der Servlet-Spezifikation ist der Webapp-Loader etwas anders. Er sucht zuerst in seiner eigenen Ressourcenbibliothek, anstatt nach oben zu delegieren, wodurch der Standarddelegierungsmechanismus unterbrochen wird. Werfen wir einen Blick auf das Design und die Implementierung von Tomcat. 2. Entwurf des Tomcat-Klassenladers Die allgemeine Klassenladerstruktur von Tomcat ist wie folgt: Die von JDK bereitgestellten Klassenlader sind: Bootstrap – Bootstrap-Klassenlader, Teil der JVM, lädt bestimmte Dateien in das Verzeichnis <JAVA_HOME>/lib/. Erweiterung – Erweiterungsklassenlader, lädt Klassenbibliotheken in das Verzeichnis <JAVA_HOME>/lib/ext/. Anwendung – Anwendungsklassenlader, auch Systemklassenlader genannt, lädt durch CLASSPATH angegebene Klassenbibliotheken. Die von Tomcat implementierten Klassenlader sind: Common – Der übergeordnete Loader ist AppClassLoader, der standardmäßig die Klassenbibliotheken im Verzeichnis ${catalina.home}/lib/ lädt. Catalina – Der übergeordnete Loader ist der Common-Klassenloader, der die von server.loader in der Konfigurationsdatei catalina.properties konfigurierten Ressourcen lädt. Dies sind im Allgemeinen die von Tomcat intern verwendeten Ressourcen. Shared – Der übergeordnete Loader ist der Common-Klassenloader, der die von shared.loader in der Konfigurationsdatei catalina.properties konfigurierten Ressourcen lädt. Dies sind im Allgemeinen die von allen Web-Anwendungen gemeinsam genutzten Ressourcen. WebappX – Der übergeordnete Loader ist der Shared Loader, der die Klassen in /WEB-INF/classes und die JAR-Pakete in /WEB-INF/lib/ lädt. JasperLoader – Der übergeordnete Loader ist der Webapp-Loader, der die durch das Kompilieren von JSP-Anwendungen generierten Klassendateien im Arbeitsverzeichnis lädt. In der Implementierung handelt es sich beim obigen Diagramm nicht um eine Vererbungsbeziehung, sondern um eine Eltern-Kind-Beziehung durch Kombination. Quellcode-Klassendiagramm des Tomcat-Klassenladers: Common, Catalina und Shared sind alle Instanzen von StandardClassLoader. Standardmäßig beziehen sie sich auf dasselbe Objekt. Es gibt keinen Unterschied zwischen StandardClassLoader und URLClassLoader. WebappClassLoader sucht und lädt gemäß der Spezifikation in der folgenden Reihenfolge: Geladen aus dem Bootstrap-Repository innerhalb der JVM. Geladen aus dem Anwendungsladerpfad, d. h. CLASSPATH. Geladen aus dem Verzeichnis /WEB-INF/classes im Webprogramm. Aus der JAR-Datei in /WEB-INF/lib im Webprogramm. Geladen aus dem Container. Gemeinsames Loader-Repository, d. h. die von allen Webprogrammen gemeinsam genutzten Ressourcen. Schauen wir uns als Nächstes die Implementierung des Quellcodes an. 3. Initialisierung des benutzerdefinierten Loaders Der allgemeine Klassenlader wird in initClassLoaders von Bootstrap initialisiert. Der Quellcode lautet wie folgt: private void initClassLoaders() { versuchen { commonLoader = createClassLoader("common", null); wenn( commonLoader == null ) { // keine Konfigurationsdatei, standardmäßig dieser Loader – wir befinden uns möglicherweise in einer „einzelnen“ Umgebung. commonLoader=dies.getClass().getClassLoader(); } //Geben Sie das Präfix der Konfigurationsdatei des Repository-Pfads und den übergeordneten Loader an und erstellen Sie eine ClassLoader-Instanz catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("geteilt", commonLoader); } fangen (Wurfbares t) { log.error("Beim Erstellen des Klassenladers kam es zu einer Ausnahme", t); System.exit(1); } } Sie können sehen, dass drei Klassenlader erstellt werden. CreateClassLoader erhält die Ressourcenlageradresse gemäß der Konfiguration und gibt schließlich eine StandardClassLoader-Instanz zurück. Der Kerncode lautet wie folgt: privater ClassLoader createClassLoader (String-Name, übergeordneter ClassLoader) wirft Ausnahme { Zeichenfolgenwert = CatalinaProperties.getProperty(name + ".loader"); wenn ((Wert == null) || (Wert.gleich(""))) return parent; // Wenn nicht konfiguriert, gib den übergebenen übergeordneten Loader zurück. ArrayList repositoryLocations = new ArrayList(); ArrayList repositoryTypes = neue ArrayList(); ... // Den Pfad zum Ressourcen-Repository abrufen String[] locations = (String[]) repositoryLocations.toArray(new String[0]); Integer[] Typen = (Integer[]) repositoryTypes.toArray(neue Integer[0]); //Erstellen Sie ein StandardClassLoader-Objekt ClassLoader classLoader = ClassLoaderFactory.createClassLoader (Standorte, Typen, übergeordnetes Element); ... gib classLoader zurück; } Nachdem der Klassenlader initialisiert wurde, wird ein Catalina-Objekt erstellt und seine Lademethode wird schließlich aufgerufen, um server.xml zu analysieren und die internen Komponenten des Containers zu initialisieren. In welcher Beziehung steht also ein Container wie Engine zum übergeordneten Loader dieser Einstellung? Das Catalina-Objekt hat eine Mitgliedsvariable parentClassLoader, die der übergeordnete Loader aller Komponenten ist. Der Standardwert ist AppClassLoader. Wenn dieses Objekt erstellt wird, wird seine Methode setParentClassLoader berücksichtigt und der übergeordnete Loader wird auf sharedLoader gesetzt. Wenn die Container-Engine der obersten Ebene in Tomcat initialisiert wird, verfügt Digester über eine SetParentClassLoaderRule-Regel, die den parentClassLoader von Catalina über die Methode Engine.setParentClassLoader verknüpft. 4. So unterbrechen Sie den übergeordneten Delegationsmechanismus Die Antwort besteht darin, Thread.getContextClassLoader() zu verwenden – den Kontextlader für den aktuellen Thread, der dynamisch festgelegt werden kann, während Ihr Code über Thread.setContextClassLoader() ausgeführt wird. Standardmäßig wird der Thread-Kontextlader vom übergeordneten Thread geerbt. Dies bedeutet, dass der Standardkontextlader aller Threads derselbe ist wie der des zuerst gestarteten Threads, d. h. des Hauptthreads, und sein Kontextlader ist AppClassLoader. Beim Start von StandardContext initialisiert Tomcat zunächst einen WebappClassLoader und setzt ihn dann als Kontextlader des aktuellen Threads. Abschließend kapselt er ihn als Loader-Objekt ein und verwendet ihn beim Laden von Servlet-Klassen mithilfe der Eltern-Kind-Beziehung zwischen Containern. 5. Klassenladen für Webanwendungen Das Laden der Klassen der Webanwendung wird durch die WebappClassLoader-Methode loadClass(String, boolean) abgeschlossen. Der Kerncode lautet wie folgt: Überschreiben von J2SE verhindern öffentliche synchronisierte Klasse loadClass (String-Name, Boolesche Auflösung) wirft ClassNotFoundException { ... Klasse clazz = null; // (0) Prüfen, ob es in den internen Cache geladen wurde clazz = findLoadedClass0(name); if (clazz != null) { wenn (log.isDebugEnabled()) log.debug("Klasse aus Cache zurückgeben"); wenn (auflösen) AuflöseKlasse(clazz); Rückkehr (Clazz); } // (0.1) Prüfen, ob es bereits in den JVM-Cache geladen ist clazz = findLoadedClass(name); if (clazz != null) { wenn (log.isDebugEnabled()) log.debug("Klasse aus Cache zurückgeben"); wenn (auflösen) AuflöseKlasse(clazz); Rückkehr (Clazz); } // (0.2) Versuchen Sie, den Systemklassenlader zu verwenden, um das Überschreiben der J2SE-Klasse zu verhindern. Versuchen Sie { clazz = system.loadClass(name); if (clazz != null) { wenn (auflösen) AuflöseKlasse(clazz); Rückkehr (Clazz); } } catch (ClassNotFoundException e) { // Ignorieren } // (0.5) Verwenden Sie SecurityManager, um zu überprüfen, ob Sie Zugriff auf diese Klasse haben, 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); } } } Boolescher Wert delegateLoad = Delegierter || Filter(Name); // (1) Ob an die übergeordnete Klasse delegiert werden soll, der Standardwert ist hier false wenn (delegateLoad) { ... } // (2) Versuche, dein eigenes Repository zu finden und zu laden try { clazz = Klasse finden(Name); if (clazz != null) { wenn (log.isDebugEnabled()) log.debug("Klasse aus lokalem Repository laden"); wenn (auflösen) AuflöseKlasse(clazz); Rückkehr (Clazz); } } Fang (ClassNotFoundException e) {} // (3) Wenn das Laden an dieser Stelle immer noch fehlschlägt, delegiere die Ladeanforderung an den übergeordneten Loader if (!delegateLoad) { wenn (log.isDebugEnabled()) log.debug("Delegieren an übergeordneten Klassenlader am Ende: " + übergeordneter Klassenlader); ClassLoader-Lader = übergeordnetes Element; wenn (Loader == null) Lader = System; versuchen { clazz = loader.loadClass(name); if (clazz != null) { wenn (log.isDebugEnabled()) log.debug("Klasse vom übergeordneten Element wird geladen"); wenn (auflösen) AuflöseKlasse(clazz); Rückkehr (Clazz); } } Fang (ClassNotFoundException e) {} } //Schließlich schlägt das Laden fehl und eine Ausnahme wird ausgelöst. throw new ClassNotFoundException(name); } Um das Überschreiben von J2SE-Klassen zu verhindern, verwendet Tomcat 6 den AppClassLoader. Die rt.jar-Kernklassenbibliothek wird vom Bootstrap Classloader geladen, dieser Loader ist jedoch im Java-Code nicht verfügbar. In höheren Versionen wurden die folgenden Optimierungen vorgenommen: ClassLoader j = String.class.getClassLoader(); wenn (j == null) { j = getSystemClassLoader(); während (j.getParent() != null) { j = j.getParent(); } } dies.javaseClassLoader = j; Beim Laden von Klassen verwendet Tomcat 6 den AppClassLoader. Die rt.jar-Kernklassenbibliothek wird vom Bootstrap Classloader geladen, dieser Loader kann jedoch nicht im Java-Code abgerufen werden. In höheren Versionen wurden die folgenden Optimierungen vorgenommen: ClassLoader j = String.class.getClassLoader(); wenn (j == null) { j = getSystemClassLoader(); während (j.getParent() != null) { j = j.getParent(); } } dies.javaseClassLoader = j; Das heißt, verwenden Sie einen Klassenlader, der dem Bootstrap-Lader möglichst nahe kommt. 6. Zusammenfassung Ich glaube, die meisten Leute sind schon einmal auf die Ausnahme ClassNotFoundException gestoßen. Dahinter steckt der Klassenlader. Ein gewisses Verständnis des Ladeprinzips hilft bei der Behebung des Problems. Oben ist die vom Herausgeber eingeführte Implementierungsmethode und der Beispielcode des Tomcat-Klassenladers. Ich hoffe, es wird allen helfen. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht und der Herausgeber wird Ihnen rechtzeitig antworten. Ich möchte auch allen für ihre Unterstützung der Website 123WORDPRESS.COM danken! Das könnte Sie auch interessieren:
|
<<: Zehn nützliche und einfache MySQL-Funktionen
>>: So fügen Sie in JS eine Abbruchfunktion zu einem Versprechen hinzu
1. Warum maxPostSize festlegen? Der Tomcat-Contai...
Viele Konzepte im UI-Design mögen in der Formulie...
Vorwort Im vorherigen Artikel wurde die Installat...
Zunächst müssen Sie bestimmen, welche Felder oder...
Softwareversion Windows: Windows 10 MySQL: mysql-...
Denken Sie im Großen und im Kleinen und lenken Si...
CentOS 6 und frühere Versionen stellen MySQL-Serv...
1. MySQL herunterladen 1. Melden Sie sich auf der...
Was ist die Nginx-Zugriffsbeschränkungskonfigurat...
1. es-Startbefehl: docker run -itd -e TAKE_FILE_O...
Grafisches Tutorial zur Installation und Konfigur...
01. Befehlsübersicht Der Befehl gcc verwendet den...
Ich habe gesehen, dass die Taobao-Webseite Import ...
1 Laden Sie das komprimierte Paket der MySQL 5.6-...
Inhaltsverzeichnis 1. Subroutensyntax 2. Beispiel...