Heim  >  Artikel  >  Java  >  Interviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?

Interviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?

Java后端技术全栈
Java后端技术全栈nach vorne
2023-08-17 14:36:381008Durchsuche

Vor ein paar Tagen wurde einem Freund während eines Vorstellungsgesprächs eine Frage zum Thema Klassenbelastung gestellt. Er beantwortete Fragen im Zusammenhang mit der Klassenbelastung regelmäßig und konnte damit umgehen, solange er sich die Interviewfragen ein wenig einprägte .

Aber wenn man tiefer fragt, nur ein bisschen tiefer, kann man eine große Anzahl von Menschen abkühlen.

Zum Beispiel: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?

Um zu verhindern, dass Sie während des Vorstellungsgesprächs auf eine solche Situation stoßen, wird dieser Artikel diese Angelegenheit regeln.

Wir werden es in 4 Teilen besprechen:

  1. Was ist der Klassenlademechanismus?
  2. Was ist das Elternterminmodell?
  3. Wie kann man das übergeordnete Delegationsmodell durchbrechen?
  4. Wie ist der Tomcat-Klassenlader konzipiert?

Ich denke, bevor wir uns mit dem Laden von Tomcat-Klassen befassen, sollten wir den Java-Standard-Klassenlader überprüfen oder konsolidieren. Als ich anfing, JVM zu lernen, war ich verwirrt über den Mechanismus zum Laden von Klassen, daher möchte ich diese Gelegenheit nutzen, um ihn mit Ihnen zu teilen.

1. Was ist ein Klassenlademechanismus?

Das Konvertieren des Ergebnisses der Codekompilierung von lokalem Maschinencode in Bytecode ist ein kleiner Schritt im Speicherformat, aber ein großer Schritt in der Entwicklung von Programmiersprachen.

Die Java Virtual Machine lädt die Daten, die die Klasse beschreiben, aus der Klassendatei in den Speicher, überprüft die Daten, konvertiert, analysiert und initialisiert die Daten und bildet schließlich einen Java-Typ, der direkt von der virtuellen Maschine verwendet werden kann Der Klassenlademechanismus der virtuellen Maschine.

Das Designteam der virtuellen Maschine implementierte die Aktion „Erhalten eines binären Bytestroms, der diese Klasse durch den vollständig qualifizierten Namen einer Klasse beschreibt“ in der Klassenladephase außerhalb der Java Virtual Machine, damit die Anwendung entscheiden kann, wie sie vorgeht . Holen Sie sich die erforderliche Klasse. Das Codemodul, das diese Aktion implementiert, wird als „Klassenlader“ bezeichnet.

Die Beziehung zwischen Klassen und Klassenladern

Obwohl der Klassenlader nur zum Implementieren der Ladeaktion von Klassen verwendet wird, beschränkt sich seine Rolle in Java-Programmen bei weitem nicht auf die Klassenladephase. Für jede Klasse müssen der Klassenlader, der sie lädt, und die Klasse selbst ihre Einzigartigkeit in der Java Virtual Machine herstellen. Jeder Klassenlader verfügt über einen unabhängigen Klassennamensraum.

Dieser Satz kann auf eine populärere Weise ausgedrückt werden: Der Vergleich, ob zwei Klassen „gleich“ sind, macht nur dann Sinn, wenn die beiden Klassen vom selben Klassenlader geladen werden, ansonsten auch dann, wenn die beiden Klassen aus derselben Klassendatei stammen Solange die Klassenlader, die sie laden, unterschiedlich sind, dürfen die beiden Klassen nicht gleich sein.

2. Was ist das Parental Delegation Model? 1 Aus Sicht der Java Virtual Machine gibt es nur zwei verschiedene Klassenlader: Einer ist der Bootstrap ClassLoader, der die C++-Sprache verwendet der virtuellen Maschine selbst; der andere sind alle anderen Klassenlader, die in der Java-Sprache unabhängig von der virtuellen Maschine implementiert sind und alle von abstrakten Klassen erben.

2 Aus der Sicht von Java-Entwicklern kann das Laden von Klassen in weitere Details unterteilt werden. Die meisten Java-Programmierer verwenden die folgenden drei vom System bereitgestellten Klassenlader: java.lang.ClassLoader

  • Bootstrap ClassLoader: Dieser Klassenladerkomplex wird im Verzeichnis JAVA_HOME/lib oder in dem durch den Parameter -Xbootclasspath angegebenen Pfad gespeichert und von der virtuellen Maschine erkannt (nur anhand der Dateinamenerkennung, z. B rt.jar, Klassenbibliotheken mit inkonsistenten Namen werden nicht überlastet, selbst wenn sie im lib-Verzeichnis abgelegt werden.
  • Extension ClassLoader: Dieser Klassenlader besteht aus sun.misc.Launcher$ExtClassLoader-Implementierung, die für das Mischen aller Klassenbibliotheken im Verzeichnis JAVA_HOME/lib/ext oder in dem durch die Systemvariable java.ext.dirs angegebenen Pfad verantwortlich ist. Entwickler können den Erweiterungsklassenlader direkt verwenden. sun.misc.Launcher$ExtClassLoader实现,它负责夹杂JAVA_HOME/lib/ext 目录下的,或者被java.ext.dirs 系统变量所指定的路径中的所有类库。开发者可以直接使用扩展类加载器。
  • 应用程序类加载器(Application ClassLoader): 这个类加载器由sun.misc.Launcher$AppClassLoader

Application ClassLoader:

Dieser Klassenlader besteht aus sun.misc.Launcher$AppClassLoaderImplementierung. Da dieser Klassenlader der Rückgabewert der getSystemClassLoader-Methode in ClassLoader ist, wird er auch zum Systemklassenlader. Es ist für das Laden der im Benutzerklassenpfad (ClassPath) angegebenen Klassenbibliothek verantwortlich. Entwickler können diesen Klassenlader direkt verwenden. Wenn die Anwendung keinen eigenen Klassenlader definiert, ist dies normalerweise der Standard-Klassenlader im Programm.
Interviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?
Die Beziehung zwischen diesen Klassenladern ist im Allgemeinen wie in der folgenden Abbildung dargestellt:

Bild

Die Beziehung zwischen den einzelnen Klassenladern in der Abbildung wird als

Elterndelegierungsmodell des Klassenladers
(Parents Dlegation Mode) bezeichnet ). Das übergeordnete Delegationsmodell erfordert, dass zusätzlich zum Startklassenlader der obersten Ebene alle anderen Klassenlader von ihren eigenen übergeordneten Klassenladern geladen werden. Die Eltern-Kind-Beziehung zwischen Klassenladern wird hier im Allgemeinen nicht durch Vererbung, sondern durch Vererbung realisiert Sie alle verwenden Kompositionsbeziehungen, um den Code des übergeordneten Loaders wiederzuverwenden.

🎜Hinweis: Während des Interviews fragt der Interviewer möglicherweise auch, welches Verzeichnis der entsprechende Klassenlader lädt. 🎜🎜🎜Das übergeordnete Delegationsmodell von Klassenladern wurde während JDK1.2 eingeführt und wird in allen nachfolgenden Java-Programmen häufig verwendet. Es handelt sich jedoch nicht um ein obligatorisches Einschränkungsmodell, sondern wird von Java-Designern für die Implementierung eines Klassenladers empfohlen. 🎜

Der Arbeitsprozess des übergeordneten Delegationsmodells ist: Wenn ein Klassenlader eine Klassenladeanforderung erhält, versucht er nicht, zuerst die Klasse selbst zu laden, sondern delegiert die Anforderung zur Vervollständigung an den übergeordneten Klassenlader. Dies gilt für jede Ebene des Klassenladers, daher sollten alle Ladeanfragen schließlich nur dann an den Startklassenlader der obersten Ebene gesendet werden, wenn der übergeordnete Lader meldet, dass er die Anfrage nicht abschließen kann (die erforderliche Anfrage wird nicht in seinem Suchbereich gefunden). )-Klasse), versucht der Subloader, sie selbst zu laden.

Warum machst du das?

Wenn das übergeordnete Delegationsmodell nicht verwendet wird und jeder Klassenlader es selbst lädt und der Benutzer eine Klasse namens java.lang.Object schreibt und sie in den ClassPath des Programms einfügt, verfügt das System über mehrere verschiedene Objektklassen. Das grundlegendste Verhalten im Java-Typsystem kann nicht garantiert werden. Auch Bewerbungen werden zum Chaos.

Wie wird das übergeordnete Delegationsmodell umgesetzt?

ist sehr einfach: Der gesamte Code befindet sich in der LoadClass-Methode in java.lang.ClassLoader. Der Code lautet wie folgt:

Interviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?
Bild

Die Logik ist klar und leicht zu verstehen: Überprüfen Sie zunächst, ob es geladen wurde Wenn nicht, rufen Sie die LoadClass-Methode des übergeordneten Laders auf. Wenn der übergeordnete Lader leer ist, wird standardmäßig der Startklassenlader als übergeordneter Lader verwendet. Wenn das Laden der übergeordneten Klasse fehlschlägt, lösen Sie eine ClassNotFoundException-Ausnahme aus und rufen Sie dann ihre eigene findClass-Methode auf, um sie zu laden.

3. Wie kann man das übergeordnete Delegationsmodell durchbrechen?

Wir haben gerade gesagt, dass das übergeordnete Delegationsmodell kein obligatorisches Einschränkungsmodell ist, sondern eine empfohlene Klassenlader-Implementierung. Die meisten Klassenlader in der Java-Welt folgen diesem Modell, es gibt jedoch Ausnahmen. Bisher wurde das übergeordnete Delegationsmodell dreimal im großen Stil „gebrochen“.

Das erste Mal: ​​vor dem Aufkommen des elterlichen Delegationsmodells ----- vor der Veröffentlichung von JDK1.2.

Zweites Mal: ​​Es liegt an den Mängeln dieses Modells selbst. Wir sagen, dass das übergeordnete Delegationsmodell das Problem der Vereinheitlichung der Basisklassen jedes Klassenladers gut löst (die Basisklassen werden vom übergeordneten Lader geladen, weil sie „Basisklassen“ heißen). Es handelt sich immer um eine API, die vom Benutzercode aufgerufen wird, es gibt jedoch keine Garantie. Was passiert, wenn die Basisklasse Benutzercode aufruft?

Es ist nicht unmöglich. Ein typisches Beispiel ist der JNDI-Dienst, der jetzt ein Standarddienst in Java ist. Sein Code wird vom Startup-Klassenlader (rt.jar, der in JDK1.3 eingefügt wurde) geladen, er muss jedoch von einem unabhängigen Dienst aufgerufen und implementiert werden Anbieter. Und stellen Sie den JNDI-Schnittstellenanbietercode (SPI, Service Provider Interface) unter dem ClassPath der Anwendung bereit, aber es ist für den Startklassenlader unmöglich, diese Codes zu „kennen“. Weil diese Klassen nicht in rt.jar , Der Startklassenlader muss jedoch erneut geladen werden. Was zu tun? rt.jar中,但是启动类加载器又需要加载。怎么办呢?

为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread

Um dieses Problem zu lösen, musste das Java-Designteam ein weniger elegantes Design einführen: Thread Context ClassLoader. Dieser Klassenlader kann ,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">java übergeben werden. lang.Thread Die setContextClassLoader-Methode der Code>-Klasse ist festgelegt. Wenn es beim Erstellen des Threads nicht festgelegt wurde, erbt es eines vom übergeordneten Thread. Wenn es im globalen Bereich der Anwendung nicht zu stark festgelegt ist, wird dieser Klassenlader standardmäßig auf den Anwendungsklassenlader gesetzt.

Hey, mit dem Thread-Kontextlader verwendet der JNDI-Dienst diesen Thread-Kontextlader, um den erforderlichen SPI-Code zu laden, dh der übergeordnete Klassenlader fordert den untergeordneten Klassenlader auf, die Klassenladeaktion abzuschließen. Dieses Verhalten wird tatsächlich geöffnet Die hierarchische Struktur des übergeordneten Delegationsmodells verwendet den Klassenlader umgekehrt, was tatsächlich gegen die allgemeinen Prinzipien des übergeordneten Delegationsmodells verstößt. Aber Sie können nichts dagegen tun. Alle Ladeaktionen, die SPI in Java beinhalten, verwenden grundsätzlich diese Methode. Zum Beispiel JNDI, JDBC, JCE, JAXB, JBI usw.

Das dritte Mal: ​​Um Hot-Plugging, Hot-Deployment und Modularisierung zu erreichen, was bedeutet, dass eine Funktion hinzugefügt oder eine Funktion ohne Neustart entfernt werden muss, müssen Sie nur das Modul zusammen mit dem Klassenlader austauschen, um eine Hot-Code-Ersetzung zu erreichen.

Das Buch sagt auch:

Grundsätzlich besteht in Java-Programmen ein Konsens: Die Verwendung von Klassenladern durch OSGI ist es wert, erlernt zu werden. Das Verständnis der Implementierung von OSGI kann als Beherrschung der Essenz von Klassenladern angesehen werden.

Super! ! !

Jetzt haben wir im Grunde das Funktionsprinzip des Java-Standardklassenladens verstanden und kennen auch das übergeordnete Delegationsmodell. Nachdem ich so viel gesagt habe, hätte ich unseren Tomcat fast vergessen. Unser Thema ist, warum der Tomcat-Loader gegen das elterliche Delegationsmodell verstößt.

Lassen Sie uns über unseren Tomcat-Klassenlader sprechen.

4. Wie ist der Klassenlader von Tomcat konzipiert?

Stellen wir zunächst eine Frage:

Ist es in Ordnung, wenn Tomcat den Standardmechanismus zum Laden von Klassen verwendet?

🎜

Lassen Sie uns darüber nachdenken: Tomcat ist ein Webcontainer. Welche Probleme muss er also lösen:

  • Ein Webcontainer muss möglicherweise zwei Anwendungen bereitstellen, und verschiedene Anwendungen können auf unterschiedliche Versionen derselben Drittanbieterklasse angewiesen sein Bibliothek Es ist nicht erforderlich, dass sich nur eine Kopie derselben Klassenbibliothek auf demselben Server befindet. Daher muss sichergestellt werden, dass die Klassenbibliotheken jeder Anwendung unabhängig und voneinander isoliert sind.
  • Die gleiche Klassenbibliothek und die gleiche Version, die im gleichen Webcontainer bereitgestellt werden, können gemeinsam genutzt werden. Andernfalls, wenn der Server über 10 Anwendungen verfügt, müssen 10 Kopien derselben Klassenbibliothek in die virtuelle Maschine geladen werden, was Unsinn ist.
  • Der Webcontainer verfügt außerdem über eigene abhängige Klassenbibliotheken, die nicht mit den Klassenbibliotheken der Anwendung verwechselt werden können. Aus Sicherheitsgründen sollte die Klassenbibliothek des Containers von der Klassenbibliothek des Programms isoliert werden.
  • Der Webcontainer muss die Änderung von JSP unterstützen. Wir wissen, dass die JSP-Datei schließlich in eine Klassendatei kompiliert werden muss, bevor sie in einer virtuellen Maschine ausgeführt werden kann Laufen. Was hättest du sonst davon? Daher muss der Webcontainer die JSP-Änderung ohne Neustart unterstützen.

Schauen wir uns noch einmal unsere Frage an: Ist es in Ordnung, wenn Tomcat den Standardmechanismus zum Laden von Klassen verwendet?

Die Antwort ist nein. Warum? Schauen wir uns das erste Problem an. Wenn Sie den Standard-Klassenlademechanismus verwenden, können Sie nicht zwei verschiedene Versionen derselben Klassenbibliothek laden, unabhängig davon, welchen Versionsnamen Sie haben Kopie.

Die zweite Frage ist, dass der Standard-Klassenlader implementiert werden kann, da seine Aufgabe darin besteht, die Eindeutigkeit sicherzustellen. Die dritte Frage ist dieselbe wie die erste Frage. Schauen wir uns die vierte Frage noch einmal an. Wir überlegen, wie wir eine Hot-Modifikation von JSP-Dateien implementieren (der im Poster angegebene Name ist also eine Klassendatei). Ebenso wird der Klassenlader direkt abgerufen. Wenn die JSP bereits im Methodenbereich vorhanden ist, wird die geänderte JSP nicht erneut geladen.

Was sollen wir also tun? Wir können den Klassenlader dieser JSP-Datei direkt deinstallieren. Sie sollten also gedacht haben, dass jede JSP-Datei einem eindeutigen Klassenlader entspricht. Wenn eine JSP-Datei geändert wird, wird der JSP-Klassenlader direkt entladen. Erstellen Sie den Klassenlader neu und laden Sie die JSP-Datei neu.

Wie implementiert Tomcat seinen eigenen einzigartigen Klassenlademechanismus?

Wie implementiert Tomcat es? Das großartige Tomcat-Team hat es bereits entworfen. Werfen wir einen Blick auf ihre Konstruktionszeichnungen:

Interviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?
Bild

Wir sehen, dass die ersten drei Klassenladeprogramme mit den Standardladeprogrammen CommonClassLoader, CatalinaClassLoader und WebappClassLoader übereinstimmen. Sie laden jeweils /common/*/server/*/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader: private Klassenladeprogramme für jede Webanwendung Der Ladepfad ist nur für die aktuelle Webanwendung sichtbar.

Aus der Delegationsbeziehung in der Abbildung ist ersichtlich:

Die Klassen, die von CommonClassLoader geladen werden können, können von Catalina ClassLoader und SharedClassLoader verwendet werden, wodurch die Verwendung realisiert wird von öffentlichen Klassenbibliotheken Shared, während die Klassen, die CatalinaClassLoader und Shared ClassLoader laden können, voneinander isoliert sind.

WebAppClassLoader kann die von SharedClassLoader geladenen Klassen verwenden, aber einzelne WebAppClassLoader-Instanzen sind voneinander isoliert.

Der Ladebereich von JasperLoader ist nur die von dieser JSP-Datei kompilierte .Class-Datei. Der Zweck ihres Erscheinens besteht darin, sie zu verwerfen: Wenn der Webcontainer erkennt, dass die JSP-Datei geändert wurde, ersetzt sie die aktuelle JasperLoader-Instanz. , und implementieren Sie die HotSwap-Funktion von JSP-Dateien, indem Sie einen neuen Jsp-Klassenlader erstellen.

Okay, bisher wissen wir bereits, warum Tomcat so gestaltet ist und wie es gestaltet ist. Verstößt Tomcat also gegen das von Java empfohlene übergeordnete Delegationsmodell? Die Antwort lautet: verletzt.

Wir haben bereits gesagt:

Das übergeordnete Delegationsmodell erfordert, dass mit Ausnahme des Startklassenladers der obersten Ebene alle anderen Klassenlader von ihrem eigenen übergeordneten Klassenlader geladen werden.

Tomcat ist offensichtlich nicht auf diese Weise implementiert. Um eine Isolation zu erreichen, entspricht Tomcat dieser Konvention nicht. Jeder webappClassLoader lädt die Klassendatei in sein eigenes Verzeichnis und übergibt sie nicht an den übergeordneten Klassenlader.

Wir erweitern eine Frage: Was sollen wir tun, wenn der Common ClassLoader von Tomcat Klassen in WebApp ClassLoader laden möchte? Nachdem wir den vorherigen Inhalt zum Zerstören des übergeordneten Delegationsmodells gelesen haben, haben wir eine klare Vorstellung davon, dass wir den Thread-Kontext-Klassenlader verwenden können, um ihn zu implementieren Ladevorgang.

Ist es nicht großartig?

Zusammenfassung

Okay, endlich verstehen wir, warum Tomcat gegen das übergeordnete Delegationsmodell verstößt, und wir wissen auch, wie der Klassenlader von Tomcat entworfen ist. Übrigens habe ich den Standard-Klassenlademechanismus von Java überprüft und auch gelernt, wie man den Klassenlademechanismus von Java zerstört. Diesmal ist die Ernte nicht gering! ! !

Das obige ist der detaillierte Inhalt vonInterviewer: Warum verstößt der Tomcat-Klassenlader gegen das übergeordnete Delegationsmodell?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Java后端技术全栈. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen