数日前、友人が面接でクラス ローディングについて質問されました。彼はクラス ローディングに関する質問に普通に答えており、覚えていれば対処できました。面接の質問は少しあります。
しかし、もう少し深く質問すれば、多くの人を冷やすことができます。
例: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?
あなたが面接中にこのような事態に遭遇しないように、この記事ではこの件について整理していきます。
これを 4 つの部分に分けて説明します。
Tomcat のクラスローディングを学ぶ前に、Java のデフォルトのクラスローダーを確認または統合する必要があると思います。私が初めて JVM を学び始めたとき、クラスロードの仕組みについて混乱したので、この機会に共有したいと思います。
コードのコンパイル結果をローカル マシン コードからバイトコードに変換することは、ストレージ形式では小さなステップですが、プログラミング言語の開発では大きなステップです。
Java 仮想マシンは、クラスを記述するデータを Class ファイルからメモリにロードし、データを検証し、変換、解析、初期化して、最終的に仮想マシンで直接使用できる Java 型を形成します。 . これは仮想マシンの機能であり、クラスローディングメカニズムです。
仮想マシン設計チームは、Java 仮想マシンの外部のクラス読み込みフェーズで「クラスの完全修飾名を通じてこのクラスを記述するバイナリ バイト ストリームを取得する」アクションを実装しました。これにより、アプリケーションは次のことを決定します。必要なクラスを取得する方法は自分で決めてください。このアクションを実装するコード モジュールは「クラス ローダー」と呼ばれます。
クラス ローダーはクラスのロード アクションを実装するためにのみ使用されますが、Java プログラムにおけるその役割はクラスのロード段階に限定されるわけではありません。どのクラスでも、それをロードするクラス ローダーとクラス自体は、Java 仮想マシン内でその一意性を確立する必要があり、各クラス ローダーは独立したクラス名前空間を持ちます。
この文は、より一般的な方法で表現できます: 2 つのクラスが「等しい」かどうかの比較は、2 つのクラスが同じクラス ローダーによってロードされた場合にのみ意味を持ちます。それ以外の場合、たとえこの Two クラスが同じクラス ファイルであり、同じ仮想マシンによってロードされます。それらをロードするクラス ローダーが異なる限り、2 つのクラスは等価であってはなりません。
1. Java 仮想マシンの観点から見ると、異なるクラス ローダーは 2 つだけあります。1 つはブート クラス ローダー (Bootstrap ClassLoader) です。このクラス ローダーは C 言語 (HotSpot のみ) で実装され、仮想マシン自体の一部です。もう 1 つは他のすべてのクラス ローダーで、Java 言語で実装され、仮想マシンから独立しており、すべて抽象クラスから継承します。 java.lang.ClassLoader
.
2. Java 開発者の観点から見ると、クラスのロードはさらに詳細に分割できます。ほとんどの Java プログラマは、次の 3 つのシステム提供のクラス ローダーを使用します。 :
sun.misc.Launcher$ExtClassLoader
によって実装されます。これは、 JAVA_HOME/lib/ext ディレクトリ、または java.ext.dirs システム変数で指定されたパス。開発者は、拡張クラス ローダーを直接使用できます。 sun.misc.Launcher$AppClassLoader
によって実装されます。このクラスローダはClassLoaderのgetSystemClassLoaderメソッドの戻り値となるため、システムクラスローダにもなります。ユーザー クラス パス (ClassPath) で指定されたクラス ライブラリをロードします。開発者はこのクラス ローダーを直接使用できます。アプリケーションが独自のクラス ローダーを定義していない場合、通常、これがプログラム内のデフォルトのクラス ローダーになります。 これらのクラス ローダー間の関係は、一般に次の図に示すとおりです。画像 各クラスローダ間の関係を、クラスローダの
Parent Delegation Modelクラス ローダーの親委任モデルは JDK1.2 のときに導入され、その後のすべての Java プログラムで広く使用されていますが、これは必須の制約モデルではなく、Java 設計のクラス ローダー実装です。開発者に推奨される方法。
親委任モデルの動作プロセスは次のとおりです。クラス ローダーがクラス ロード要求を受信した場合、最初にクラス自体をロードしようとするのではなく、要求を親クラス ローダーに委任して完了します。これは、クラス ローダーのすべてのレベルに当てはまります。そのため、すべてのロード要求は、最終的には最上位の起動クラス ローダーに送信される必要があります。親ローダーが要求を完了できない (要求されたものがその検索範囲に見つからない) とフィードバックされた場合に限ります。 ) クラス)、サブローダーはそれを単独でロードしようとします。 ######どうしてそれをするの?
親委任モデルはどのように実装されますか?
のloadClass メソッド内にあり、コードは次のとおりです:
3. 親の委任モデルを打破するにはどうすればよいでしょうか?
2 回目: モデル自体の欠陥が原因でした。
親委任モデルは、各クラス ローダーの基底クラスを統合するという問題 (より基本的なクラスは上位レベルのローダーによってロードされる) をうまく解決すると言えます。基底クラスが「基本」と呼ばれる理由は次のとおりです。常にユーザーコードから呼び出される API として使用されるためですが、保証はありません。 これは不可能ではありません。典型的な例は JNDI サービスです。JNDI は現在 Java の標準サービスです。そのコードは起動クラス ローダー (JDK1.3 に組み込まれていた rt.jar) によってロードされますが、独立したプログラムによって呼び出され、実装される必要があります。そして、アプリケーションの ClassPath の下に JNDI インターフェイス プロバイダー (SPI、サービス プロバイダー インターフェイス) コードをデプロイしますが、起動クラス ローダーがこれらのコードを「認識」することは不可能です。これらのクラスは この問題を解決するために、Java 設計チームは、あまり洗練されていない設計である Thread Context ClassLoader を導入する必要がありました。このクラス ローダーは、 スレッド コンテキスト ローダーを使用すると、JNDI サービスはこのスレッド コンテキスト ローダーを使用して必要な SPI コードをロードします、つまり、親クラス ローダーが子クラス ローダーにクラス ロード アクションを完了するよう要求します。この動作は、実際には親委任モデルの階層を開いてクラス ローダーを逆に使用するもので、実際には親委任モデルの一般原則に違反します。 Java での SPI に関連するすべてのロード操作は、基本的にこのメソッドを使用します。たとえば、JNDI、JDBC、JCE、JAXB、JBI などです。 3 回目: ホットスワップ、ホットデプロイメント、およびモジュール化 (再起動せずに機能を追加または削除することを意味します) を実現するには、モジュールごと交換するだけで済みます。クラス ローダー: これにより、コードのホット リプレースが実現されます。 この本では次のことについても言及されています: Java プログラムには基本的にコンセンサスがあります: OSGI によるクラス ローダーの使用は学ぶ価値があります。OSGI の実装を理解すれば、次のことが可能になります。クラスローダーの本質をマスターしたものとみなされます。 ######素晴らしい! ! ! これで、Java のデフォルト クラス ローディングの動作原理を基本的に理解し、親委任モデルについても理解しました。ここまで言って、Tomcat のことをほとんど忘れていましたが、今回のテーマは、なぜ Tomcat ローダーが親委任モデルに違反するのかということです。 Tomcat クラスローダーについて話しましょう。 4. Tomcat のクラスローダーはどのように設計されていますか? 考えてみましょう: Tomcat は Web コンテナなので、どのような問題を解決する必要がありますか: 私たちの質問をもう一度見てみましょう: Tomcat がデフォルトのクラス読み込みメカニズムを使用しても問題ありませんか? #########答えはいいえだ。なぜ?最初の問題を見てみましょう. デフォルトのクラス ローダー メカニズムを使用する場合、同じクラス ライブラリの 2 つの異なるバージョンをロードすることはできません. デフォルトのアキュムレータは、バージョンに関係なく、完全修飾クラスのみを考慮します。コピー。 最初の 3 つのクラス ローディングがデフォルトのものと一致していることがわかります。CommonClassLoader、CatalinaClassLoader、SharedClassLoader、および WebappClassLoader は Tomcat 独自のクラス ローダーです。これらは #それぞれ ##/common/* 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 のクラスローディングメカニズムを破棄する方法も学びました。今回の収穫は少なくありません! ! ! rt.jar
に含まれていないため、クラス ローダーを開始するにはロードする必要があります。どうやってするの? java.lang.Thread
クラスの setContextClassLoader メソッドを通じて設定できます。スレッドの作成時に設定されていない場合は、親スレッドから継承されます。アプリケーションのグローバル スコープ内であまり設定されていない場合、このクラス ローダーはデフォルトのアプリケーション クラス ローダーになります。 まず最初に質問しましょう:
Tomcat がデフォルトのクラスロードメカニズムを使用しても問題ありませんか?
、
/server/*、
/shared/* (Tomcat 6 以降、これらはルート ディレクトリの lib ディレクトリにマージされました)および Java クラス ライブラリは
/WebApp/WEB-INF/* にあります。通常、WebApp クラスローダと Jsp クラスローダのインスタンスは複数存在し、各 Web アプリケーションは WebApp クラスローダに対応し、各 JSP ファイルは Jsp クラスローダに対応します。
CommonClassLoader はすべてのクラスをロードできます Catalina ClassLoader と SharedClassLoader で使用できるため、パブリック クラス ライブラリの共有が実現しますが、CatalinaClassLoader と Shared ClassLoader がロードできるクラスは互いに分離されています。
: Tomcat の最も基本的なクラス ローダー。読み込みパス内のクラスには、Tomcat コンテナ自体と各 Web アプリからアクセスできます。
まとめ
以上がインタビュアー: Tomcat クラスローダーが親委任モデルに違反するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。