ホームページ  >  記事  >  Java  >  インタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?

インタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?

Java后端技术全栈
Java后端技术全栈転載
2023-08-17 14:36:381095ブラウズ

数日前、友人が面接でクラス ローディングについて質問されました。彼はクラス ローディングに関する質問に普通に答えており、覚えていれば対処できました。面接の質問は少しあります。

しかし、もう少し深く質問すれば、多くの人を冷やすことができます。

例: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?

あなたが面接中にこのような事態に遭遇しないように、この記事ではこの件について整理していきます。

これを 4 つの部分に分けて説明します。

  1. クラス ロード メカニズムとは何ですか?
  2. 保護者予約モデルとは何ですか?
  3. 親の委任モデルを打破するにはどうすればよいでしょうか?
  4. Tomcat クラス ローダーはどのように設計されていますか?

Tomcat のクラスローディングを学ぶ前に、Java のデフォルトのクラスローダーを確認または統合する必要があると思います。私が初めて JVM を学び始めたとき、クラスロードの仕組みについて混乱したので、この機会に共有したいと思います。

1. クラスロードメカニズムとは何ですか?

コードのコンパイル結果をローカル マシン コードからバイトコードに変換することは、ストレージ形式では小さなステップですが、プログラミング言語の開発では大きなステップです。

Java 仮想マシンは、クラスを記述するデータを Class ファイルからメモリにロードし、データを検証し、変換、解析、初期化して、最終的に仮想マシンで直接使用できる Java 型を形成します。 . これは仮想マシンの機能であり、クラスローディングメカニズムです。

仮想マシン設計チームは、Java 仮想マシンの外部のクラス読み込みフェーズで「クラスの完全修飾名を通じてこのクラスを記述するバイナリ バイト ストリームを取得する」アクションを実装しました。これにより、アプリケーションは次のことを決定します。必要なクラスを取得する方法は自分で決めてください。このアクションを実装するコード モジュールは「クラス ローダー」と呼ばれます。

クラスとクラス ローダーの関係

クラス ローダーはクラスのロード アクションを実装するためにのみ使用されますが、Java プログラムにおけるその役割はクラスのロード段階に限定されるわけではありません。どのクラスでも、それをロードするクラス ローダーとクラス自体は、Java 仮想マシン内でその一意性を確立する必要があり、各クラス ローダーは独立したクラス名前空間を持ちます。

この文は、より一般的な方法で表現できます: 2 つのクラスが「等しい」かどうかの比較は、2 つのクラスが同じクラス ローダーによってロードされた場合にのみ意味を持ちます。それ以外の場合、たとえこの Two クラスが同じクラス ファイルであり、同じ仮想マシンによってロードされます。それらをロードするクラス ローダーが異なる限り、2 つのクラスは等価であってはなりません。

2. 親委任モデルとは

1. Java 仮想マシンの観点から見ると、異なるクラス ローダーは 2 つだけあります。1 つはブート クラス ローダー (Bootstrap ClassLoader) です。このクラス ローダーは C 言語 (HotSpot のみ) で実装され、仮想マシン自体の一部です。もう 1 つは他のすべてのクラス ローダーで、Java 言語で実装され、仮想マシンから独立しており、すべて抽象クラスから継承します。 java.lang.ClassLoader.

2. Java 開発者の観点から見ると、クラスのロードはさらに詳細に分割できます。ほとんどの Java プログラマは、次の 3 つのシステム提供のクラス ローダーを使用します。 :

  • ブートストラップ クラスローダー: このクラス ローダーは、JAVA_HOME/lib ディレクトリまたは -Xbootclasspath パラメーターで指定されたパスに保存され、仮想マシンによって認識されます ( rt.jar などのファイル名によってのみ認識されます。一貫性のない名前を持つクラス ライブラリは、lib ディレクトリに配置されていてもオーバーロードされません)。
  • Extension ClassLoader: このクラス ローダーは、sun.misc.Launcher$ExtClassLoader によって実装されます。これは、 JAVA_HOME/lib/ext ディレクトリ、または java.ext.dirs システム変数で指定されたパス。開発者は、拡張クラス ローダーを直接使用できます。
  • アプリケーション クラスローダー: このクラス ローダーは、sun.misc.Launcher$AppClassLoader によって実装されます。このクラスローダはClassLoaderのgetSystemClassLoaderメソッドの戻り値となるため、システムクラスローダにもなります。ユーザー クラス パス (ClassPath) で指定されたクラス ライブラリをロードします。開発者はこのクラス ローダーを直接使用できます。アプリケーションが独自のクラス ローダーを定義していない場合、通常、これがプログラム内のデフォルトのクラス ローダーになります。

これらのクラス ローダー間の関係は、一般に次の図に示すとおりです。画像 各クラスローダ間の関係を、クラスローダの

Parent Delegation Model
(Parents Dlegation Mode) と呼びます。親委任モデルでは、トップレベルの起動クラス ローダーに加えて、他のすべてのクラス ローダーが独自の親クラス ローダーによってロードされる必要があります。ここでのクラス ローダー間の親子関係は、通常、継承によって実現されるのではなく、継承によって実現されます。これらはすべて、構成関係を使用して親ローダーのコードを再利用します。 インタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?
注: 面接中、面接官は、対応するクラス ローダーがどのディレクトリをロードするかを尋ねる場合もあります。

クラス ローダーの親委任モデルは JDK1.2 のときに導入され、その後のすべての Java プログラムで広く使用されていますが、これは必須の制約モデルではなく、Java 設計のクラス ローダー実装です。開発者に推奨される方法。

親委任モデルの動作プロセスは次のとおりです。クラス ローダーがクラス ロード要求を受信した場合、最初にクラス自体をロードしようとするのではなく、要求を親クラス ローダーに委任して完了します。これは、クラス ローダーのすべてのレベルに当てはまります。そのため、すべてのロード要求は、最終的には最上位の起動クラス ローダーに送信される必要があります。親ローダーが要求を完了できない (要求されたものがその検索範囲に見つからない) とフィードバックされた場合に限ります。 ) クラス)、サブローダーはそれを単独でロードしようとします。 ######どうしてそれをするの?

親委任モデルが使用されず、各クラス ローダーがそれを独自にロードする場合、ユーザーが java.lang.Object というクラスを作成してプログラムの ClassPath に配置すると、システムは複数のさまざまなオブジェクト クラスが出現する場合、Java 型システムの最も基本的な動作は保証できません。アプリケーションもめちゃくちゃになります。

親委任モデルはどのように実装されますか?

非常に単純です。すべてのコードは

java.lang.ClassLoader

のloadClass メソッド内にあり、コードは次のとおりです:

Pictureインタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?
ロジックは明確で理解しやすいです。まず、ロードされているかどうかを確認します。ロードされていない場合は、親ローダーのloadClassメソッドを呼び出します。親ローダーが空の場合は、スタートアップ クラスが呼び出されます。デフォルトでは、loader が親として使用されます。親クラスのロードに失敗した場合は、ClassNotFoundException 例外をスローし、独自の findClass メソッドを呼び出して親クラスをロードします。

3. 親の委任モデルを打破するにはどうすればよいでしょうか?

先ほど、親委任モデルは必須の制約モデルではなく、推奨されるクラス ローダーの実装であると述べました。 Java 世界のほとんどのクラス ローダーはこのモデルに従っていますが、例外もあり、これまでに、親委任モデルは 3 回大規模に「破壊」されました。

初回: 親委任モデルの出現前、つまり JDK1.2 のリリース前。

2 回目: モデル自体の欠陥が原因でした。

親委任モデルは、各クラス ローダーの基底クラスを統合するという問題 (より基本的なクラスは上位レベルのローダーによってロードされる) をうまく解決すると言えます。基底クラスが「基本」と呼ばれる理由は次のとおりです。常にユーザーコードから呼び出される API として使用されるためですが、保証はありません。

これは不可能ではありません。典型的な例は JNDI サービスです。JNDI は現在 Java の標準サービスです。そのコードは起動クラス ローダー (JDK1.3 に組み込まれていた rt.jar) によってロードされますが、独立したプログラムによって呼び出され、実装される必要があります。そして、アプリケーションの ClassPath の下に JNDI インターフェイス プロバイダー (SPI、サービス プロバイダー インターフェイス) コードをデプロイしますが、起動クラス ローダーがこれらのコードを「認識」することは不可能です。これらのクラスは rt.jar に含まれていないため、クラス ローダーを開始するにはロードする必要があります。どうやってするの?

この問題を解決するために、Java 設計チームは、あまり洗練されていない設計である Thread Context ClassLoader を導入する必要がありました。このクラス ローダーは、java.lang.Thread クラスの setContextClassLoader メソッドを通じて設定できます。スレッドの作成時に設定されていない場合は、親スレッドから継承されます。アプリケーションのグローバル スコープ内であまり設定されていない場合、このクラス ローダーはデフォルトのアプリケーション クラス ローダーになります。

スレッド コンテキスト ローダーを使用すると、JNDI サービスはこのスレッド コンテキスト ローダーを使用して必要な SPI コードをロードします、つまり、親クラス ローダーが子クラス ローダーにクラス ロード アクションを完了するよう要求します。この動作は、実際には親委任モデルの階層を開いてクラス ローダーを逆に使用するもので、実際には親委任モデルの一般原則に違反します。 Java での SPI に関連するすべてのロード操作は、基本的にこのメソッドを使用します。たとえば、JNDI、JDBC、JCE、JAXB、JBI などです。

3 回目: ホットスワップ、ホットデプロイメント、およびモジュール化 (再起動せずに機能を追加または削除することを意味します) を実現するには、モジュールごと交換するだけで済みます。クラス ローダー: これにより、コードのホット リプレースが実現されます。

この本では次のことについても言及されています:

Java プログラムには基本的にコンセンサスがあります: OSGI によるクラス ローダーの使用は学ぶ価値があります。OSGI の実装を理解すれば、次のことが可能になります。クラスローダーの本質をマスターしたものとみなされます。 ######素晴らしい! ! !

これで、Java のデフォルト クラス ローディングの動作原理を基本的に理解し、親委任モデルについても理解しました。ここまで言って、Tomcat のことをほとんど忘れていましたが、今回のテーマは、なぜ Tomcat ローダーが親委任モデルに違反するのかということです。

Tomcat クラスローダーについて話しましょう。

4. Tomcat のクラスローダーはどのように設計されていますか?

まず最初に質問しましょう:

Tomcat がデフォルトのクラスロードメカニズムを使用しても問題ありませんか?

考えてみましょう: Tomcat は Web コンテナなので、どのような問題を解決する必要がありますか:

  • Web コンテナは 2 つのアプリケーションをデプロイする必要があり、異なるアプリケーションが依存する可能性があります。異なるバージョンのサードパーティ クラス ライブラリでは、同じサーバー上に同じクラス ライブラリのコピーを 1 つだけ必要とすることはできないため、各アプリケーションのクラス ライブラリが互いに独立し、分離されていることを確認する必要があります。
  • 同じ Web コンテナにデプロイされた同じクラス ライブラリおよび同じバージョンを共有できます。そうしないと、サーバーに 10 個のアプリケーションがある場合、同じクラス ライブラリの 10 個のコピーを仮想マシンにロードする必要がありますが、これはナンセンスです。
  • Web コンテナには独自の依存クラス ライブラリもあります。これをアプリケーションのクラス ライブラリと混同することはできません。セキュリティ上の理由から、コンテナのクラス ライブラリはプログラムのクラス ライブラリから分離する必要があります。
  • Web コンテナは、jsp の変更をサポートする必要があります。jsp ファイルは、仮想マシンで実行する前に最終的にクラス ファイルにコンパイルする必要があることがわかっています。プログラムの実行後に jsp を変更するのが一般的ですが、それ以外の場合はどのような用途がありますか?したがって、Web コンテナは再起動せずに JSP の変更をサポートする必要があります。

私たちの質問をもう一度見てみましょう: Tomcat がデフォルトのクラス読み込みメカニズムを使用しても問題ありませんか? #########答えはいいえだ。なぜ?最初の問題を見てみましょう. デフォルトのクラス ローダー メカニズムを使用する場合、同じクラス ライブラリの 2 つの異なるバージョンをロードすることはできません. デフォルトのアキュムレータは、バージョンに関係なく、完全修飾クラスのみを考慮します。コピー。

2 番目の質問は、一意性を確保する責任があるため、デフォルトのクラス ローダーを実装できるかどうかです。 3 番目の質問は最初の質問と同じです。もう一度 4 番目の質問を見てみましょう。jsp ファイル (投稿者が指定した名前) のホット変更を実装するにはどうすればよいかを考えます。jsp ファイルは実際にはクラス ファイルです。したがって、変更されてもクラス名はそのままです。同様に、クラスローダはメソッド領域にすでに存在する JSP が取得された場合、変更された JSP は再ロードされません。 ######だから何をすべきか?この jsp ファイルのクラスローダは直接アンインストールできるので、各 jsp ファイルが固有のクラスローダに対応していると考えていただければよいのですが、jsp ファイルが変更されると、jsp クラスローダが直接アンロードされます。クラスローダーを再作成し、jsp ファイルを再ロードします。

Tomcat は独自のクラス読み込みメカニズムをどのように実装しているのでしょうか?

それでは、Tomcat はどのように実装するのでしょうか?素晴らしい Tomcat チームがすでに設計しています。設計図を見てみましょう:

インタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?

最初の 3 つのクラス ローディングがデフォルトのものと一致していることがわかります。CommonClassLoader、CatalinaClassLoader、SharedClassLoader、および WebappClassLoader は Tomcat 独自のクラス ローダーです。これらは #それぞれ ##/common/*/server/*/shared/* (Tomcat 6 以降、これらはルート ディレクトリの lib ディレクトリにマージされました)および Java クラス ライブラリは /WebApp/WEB-INF/* にあります。通常、WebApp クラスローダと Jsp クラスローダのインスタンスは複数存在し、各 Web アプリケーションは WebApp クラスローダに対応し、各 JSP ファイルは Jsp クラスローダに対応します。

  • commonLoader: Tomcat の最も基本的なクラス ローダー。読み込みパス内のクラスには、Tomcat コンテナ自体と各 Web アプリからアクセスできます。
  • catalinaLoader
  • : Tomcat コンテナーのプライベート クラス ローダー、読み込みパス内のクラスは Web アプリには表示されません;
  • sharedLoader
  • : 各 Web アプリによって共有されるクラス ローダー。読み込みパス内のクラスはすべての Web アプリに表示されますが、Tomcat コンテナーには表示されません;
  • WebappClassLoader
  • :各 Web アプリ クラス ローダーにとってプライベートであり、読み込みパス内のクラスは現在の Web アプリにのみ表示されます。
    #図の委任関係からわかります:
CommonClassLoader はすべてのクラスをロードできます Catalina ClassLoader と SharedClassLoader で使用できるため、パブリック クラス ライブラリの共有が実現しますが、CatalinaClassLoader と Shared ClassLoader がロードできるクラスは互いに分離されています。

WebAppClassLoader は SharedClassLoader によってロードされたクラスを使用できますが、各 WebAppClassLoader インスタンスは相互に分離されています。

JasperLoader のロード スコープは、この JSP ファイルによってコンパイルされた .Class ファイルのみです。その外観の目的は破棄されることです。Web コンテナが JSP ファイルが変更されたことを検出すると、JSP ファイルが変更されたことを検出すると、 current JasperLoader のインスタンスを作成し、JSP ファイルの HotSwap 機能を実装するための新しい Jsp クラス ローダーを作成します。

さて、これまでのところ、Tomcat がこのように設計されている理由とその設計方法はすでにわかっています。では、Tomcat は Java が推奨する親委任モデルに違反しているのでしょうか?答えは「違反している」です。

前に述べました:

親委任モデルでは、トップレベルの起動クラス ローダーに加えて、残りのクラス ローダーもそのクラス ローダーによってロードされる必要があります。独自の親クラス サーバーがロードされます。

明らかに、tomcat はこの方法では実装されていません。分離を実現するために、tomcat はこの規約に準拠していません。各 webappClassLoader はクラス ファイルを独自のディレクトリにロードし、それを WebappClassLoader に渡しません。親クラスローダー。

質問を拡張します: Tomcat の Common ClassLoader が WebApp ClassLoader にクラスをロードしたい場合はどうすればよいですか?親委任モデルの破棄に関する前のコンテンツを読んだ後、明確なアイデアが得られました。スレッド コンテキスト クラス ローダーを使用して実装できます。スレッド コンテキスト ローダーを使用して、親クラス ローダーは子クラス ローダーにクラスの完了を要求できます。ローディングアクションです。

すごいと思いませんか?

まとめ

さて、ようやく Tomcat が親委任モデルに違反する理由がわかりました。また、Tomcat のクラス ローダーがどのように設計されているかもわかりました。ちなみに、Java のデフォルトのクラスローダーメカニズムを確認し、Java のクラスローディングメカニズムを破棄する方法も学びました。今回の収穫は少なくありません! ! !

以上がインタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はJava后端技术全栈で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。