Tomcat は内部的に複数の ClassLoader を定義し、アプリケーションとコンテナーが異なるリポジトリ内のクラスとリソースにアクセスできるようにしながら、アプリケーション間のクラスの分離を実現します。 1. Javaクラスローディングメカニズム クラスのロードとは、コンパイルされたクラス ファイルを JVM メモリ (永続世代/メタスペース) にロードすることです。 クラス ローダーがクラスの分離を実現できる理由は、2 つのクラスが同じクラス ローダーによってロードされた場合にのみ等しくなり、そうでない場合は等しくなくなるためです。 ロード時に、JVM は親委任メカニズムを使用します。クラス ローダーがクラスをロードする場合、ロード順序は次のようになります。 まず、リクエストは親ローダーに委任されます。親ローダーがロードするクラスを見つけられない場合は、独自のリポジトリを探してロードを試みます。 このメカニズムの利点は、コア クラス ライブラリが上書きされないようにできることです。 サーブレット仕様によると、Web アプリケーション ローダーは少し異なります。Web アプリケーション ローダーは、上位に委任するのではなく、まず独自のリソース ライブラリを検索し、標準の委任メカニズムを破壊します。Tomcat の設計と実装を見てみましょう。 2. Tomcatクラスローダーの設計 Tomcat の全体的なクラス ローダー構造は次のとおりです。 JDK が提供するクラス ローダーは次のとおりです。 ブートストラップ - ブートストラップ クラス ローダーは JVM の一部で、<JAVA_HOME>/lib/ ディレクトリ内の特定のファイルをロードします。 拡張機能 - 拡張機能クラス ローダーは、<JAVA_HOME>/lib/ext/ ディレクトリ内のクラス ライブラリをロードします。 アプリケーション - アプリケーション クラス ローダーはシステム クラス ローダーとも呼ばれ、CLASSPATH で指定されたクラス ライブラリをロードします。 Tomcat が実装するクラス ローダーは次のとおりです。 Common - 親ローダーは AppClassLoader で、デフォルトで ${catalina.home}/lib/ ディレクトリのクラス ライブラリをロードします。 Catalina - 親ローダーは Common クラス ローダーで、catalina.properties 構成ファイルの server.loader によって構成されたリソースをロードします。これらのリソースは通常、Tomcat によって内部的に使用されるリソースです。 Shared - 親ローダーは Common クラス ローダーで、catalina.properties 構成ファイルの shared.loader によって構成されたリソースをロードします。これらのリソースは通常、すべての Web アプリケーションによって共有されるリソースです。 WebappX - 親ローダーは Shared ローダーで、/WEB-INF/classes のクラスと /WEB-INF/lib/ の jar パッケージをロードします。 JasperLoader - 親ローダーは Webapp ローダーで、作業ディレクトリの JSP アプリケーションをコンパイルすることによって生成されたクラス ファイルをロードします。 実装上、上図は継承関係ではなく、組み合わせによる親子関係となります。 Tomcat クラス ローダーのソース コード クラス ダイアグラム: Common、Catalina、Shared はすべて StandardClassLoader のインスタンスです。デフォルトでは、それらは同じオブジェクトを参照します。 StandardClassLoader と URLClassLoader の間に違いはありません。WebappClassLoader は仕様に従って次の順序で検索して読み込みます。 JVM 内の Bootstrap リポジトリからロード アプリケーション ローダー パス (CLASSPATH) からロード Web プログラム内の /WEB-INF/classes ディレクトリからロード Web プログラム内の /WEB-INF/lib 内の jar ファイルからロード コンテナの共通ローダー リポジトリ (すべての Web プログラムで共有されるリソース) からロード 次に、ソースコードの実装を見てみましょう。 3. カスタムローダーの初期化 共通クラスローダーは、Bootstrap の initClassLoaders で初期化されます。ソースコードは次のとおりです。 プライベートvoid initClassLoaders() { 試す { commonLoader = createClassLoader("common"、null); 共通ローダーが null の場合 // 設定ファイルはありません。このローダーがデフォルトになります。単一の環境にある可能性があります。 共通ローダー = this.getClass().getClassLoader(); } //リポジトリ パス構成ファイルのプレフィックスと親ローダーを指定して、ClassLoader インスタンスを作成します。catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } キャッチ (Throwable t) { log.error("クラスローダーの作成時に例外が発生しました", t); システム終了(1); } } 3 つのクラス ローダーが作成されていることがわかります。CreateClassLoader は構成に従ってリソース ウェアハウス アドレスを取得し、最終的に StandardClassLoader インスタンスを返します。コア コードは次のとおりです。 プライベート ClassLoader createClassLoader(文字列名、ClassLoader 親) 例外をスローします { 文字列値 = CatalinaProperties.getProperty(name + ".loader"); ((値 == null) || (値.equals("")) の場合 return parent; // 設定されていない場合は、渡された親ローダーを返します ArrayList repositoryLocations = new ArrayList(); ArrayList リポジトリタイプ = 新しい ArrayList(); ... // リソースリポジトリパスを取得します。String[] locations = (String[]) repositoryLocations.toArray(new String[0]); Integer[] タイプ = (Integer[]) リポジトリタイプ.toArray(新しい Integer[0]); // StandardClassLoader オブジェクトを作成する ClassLoader classLoader = ClassLoaderFactory.createClassLoader (場所、タイプ、親); ... classLoader を返します。 } クラス ローダーが初期化されると、Catalina オブジェクトが作成され、その load メソッドが最終的に呼び出されて server.xml が解析され、コンテナーの内部コンポーネントが初期化されます。では、Engine などのコンテナーは、この設定の親ローダーとどのように関係するのでしょうか? Catalina オブジェクトには、すべてのコンポーネントの親ローダーである parentClassLoader メンバー変数があります。デフォルトは AppClassLoader です。このオブジェクトが作成されると、その setParentClassLoader メソッドが反映され、親ローダーが sharedLoader に設定されます。 Tomcat のトップレベル コンテナ エンジンが初期化されると、Digester には SetParentClassLoaderRule ルールがあり、Engine.setParentClassLoader メソッドを通じて Catalina の parentClassLoader を関連付けます。 4. 親の委任メカニズムを解除する方法 答えは、Thread.getContextClassLoader() を使用することです。これは、現在のスレッドのコンテキスト ローダーであり、コードの実行中に Thread.setContextClassLoader() を介して動的に設定できます。 デフォルトでは、スレッド コンテキスト ローダーは親スレッドから継承されます。つまり、すべてのスレッドのデフォルトのコンテキスト ローダーは最初に開始されたスレッド、つまりメイン スレッドと同じであり、そのコンテキスト ローダーは AppClassLoader です。 StandardContext が起動すると、Tomcat はまず WebappClassLoader を初期化し、それを現在のスレッドのコンテキスト ローダーとして設定します。最後に、それを Loader オブジェクトとしてカプセル化し、コンテナー間の親子関係を利用して Servlet クラスをロードするときに使用します。 5. Webアプリケーションのクラスローディング Web アプリケーションのクラスのロードは、WebappClassLoader メソッド loadClass(String, boolean) によって完了します。コア コードは次のとおりです。 J2SEの上書き防止 パブリック同期クラス loadClass(String name, boolean resolve) ClassNotFoundException をスローします { ... クラス clazz = null; // (0) 内部キャッシュにロードされているかどうかを確認します clazz = findLoadedClass0(name); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("キャッシュからクラスを返す"); もし (解決) が、resolvClass(clazz) を解決した場合; 戻る(clazz) } // (0.1) JVM キャッシュにすでにロードされているかどうかを確認します clazz = findLoadedClass(name); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("キャッシュからクラスを返す"); もし、(解決)resolveClass(clazz) が見つかったら; 戻る(clazz) } // (0.2) J2SEクラスの上書きを防ぐためにシステムクラスローダーを使用する try { clazz = system.loadClass(名前); (clazz != null)の場合{ もし、(解決)resolveClass(clazz) が見つかったら; 戻る(clazz); } } catch (ClassNotFoundException e) { // 無視 } // (0.5) SecurityManagerを使用してこのクラスへのアクセス権があるかどうかを確認します。if (securityManager != null) { int i = name.lastIndexOf('.'); もし (i >= 0) { 試す { セキュリティマネージャのパッケージアクセスをチェックします(名前のサブ文字列(0,i)); } キャッチ (SecurityException se) { 文字列エラー = "セキュリティ違反、" + を使用しようとしました 「制限クラス: 」+ 名前; log.info(エラー、se); 新しい ClassNotFoundException(error, se) をスローします。 } } } ブール型 delegateLoad = delegate || filter(name); // (1) 親クラスに委譲するかどうか。デフォルト値は false if (delegateLoad) { ... } // (2) 独自のリポジトリを見つけてロードしてみる try { clazz = findClass(名前); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("ローカルリポジトリからクラスをロードしています"); もし、(解決)resolveClass(clazz) が見つかったら; 戻る(clazz); } } キャッチ (ClassNotFoundException e) {} // (3) この時点でも読み込みが失敗する場合は、読み込み要求を親ローダーに委任します。if (!delegateLoad) { (log.isDebugEnabled())の場合 log.debug(" 最後に親クラスローダーに委任しています: " + parent); ClassLoader ローダー = 親; if (ローダー == null) ローダー = システム; 試す { clazz = loader.loadClass(名前); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("親からクラスをロードしています"); もし (解決) が、resolvClass(clazz) を解決した場合; 戻る(clazz) } } キャッチ (ClassNotFoundException e) {} } //最後に、読み込みが失敗し、例外がスローされます throw new ClassNotFoundException(name); } J2SE クラスが上書きされないようにするために、Tomcat 6 は AppClassLoader を使用します。rt.jar コア クラス ライブラリは Bootstrap Classloader によってロードされますが、このローダーは Java コードでは使用できません。上位バージョンでは、次の最適化が行われます。 クラスローダー j = String.class.getClassLoader(); j == nullの場合{ j = getSystemClassLoader(); (j.getParent() != null) の場合 { j = j.getParent(); } } this.javaseClassLoader を j に設定します。 クラスをロードするときに、Tomcat 6 は AppClassLoader を使用します。rt.jar コア クラス ライブラリは Bootstrap Classloader によってロードされますが、このローダーは Java コードでは取得できません。上位バージョンでは、次の最適化が行われます。 クラスローダー j = String.class.getClassLoader(); j == nullの場合{ j = getSystemClassLoader(); (j.getParent() != null) の場合 { j = j.getParent(); } } this.javaseClassLoader を j に設定します。 つまり、Bootstrap ローダーにできるだけ近いクラス ローダーを使用します。 6. まとめ ほとんどの人が ClassNotFoundException 例外に遭遇したことがあると思います。この背後にはクラス ローダーがあります。ローディングの原則をある程度理解しておくと、問題のトラブルシューティングに役立ちます。 上記は、編集者が紹介したTomcatクラスローダーの実装方法とサンプルコードです。皆様のお役に立てれば幸いです。ご質問がある場合は、メッセージを残してください。編集者がすぐに返信します。また、123WORDPRESS.COM ウェブサイトをサポートしてくださっている皆様にも感謝申し上げます。 以下もご興味があるかもしれません:
|
一部の Web ページは大きく見えなくても開くのに非常に時間がかかる場合があります。一方、他の We...
背景まず最初に、私はフロントエンド開発の専門家ではないことを述べておきたいと思います。私の以前のコン...
この記事では、シンプルなカルーセル効果を実現するためのjsの具体的なコードを参考までに紹介します。具...
Linux の /etc/network/interfaces ファイルは、ネットワーク インターフ...
目次1. はじめに2. 使用1. @コンポーネント2. 計算、データ、方法3. @props 4. ...
LinuxにMySQL 8.0.25をインストールするための最新のチュートリアルを参考にしてください...
私は長い間PHPに触れてきましたが、インストール環境は非常に不慣れです。多くの問題に遭遇しました。B...
1. BIOSを確認するまず、コンピュータの起動モードを確認します。win+R と入力し、msinf...
CSSでtext-align、margin: 0 autoを使用して中央揃えにするtext-alig...
Windows で Nginx を使用するには、Nginx サービスの起動、停止、Nginx のリロ...
目次序文実装のアイデア実装コード成果を達成する序文これは、テーブルを動的に追加する例です。[追加] ...
HTMLはヘッドとボディの2つの部分で構成されています** ヘッド内のタグはヘッドタグです** タイ...
この記事では主に、Vue で TodoList をカプセル化するケースと、ブラウザのローカル キャッ...
1. MySQLのトランザクションの概念MySQL トランザクションは主に、操作量が多く複雑度の高い...
目次質問:ケース(1)子スレッドを作成する前にフォークするケース(2)子スレッドを作成した後にフォー...