Tomcatが親の委任メカニズムを破壊する方法についての簡単な説明

Tomcatが親の委任メカニズムを破壊する方法についての簡単な説明

クラスをロードしようとしたときに JVM が失敗したことを示す ClassNotFound 例外が頻繁に発生します。

この例外を解決するには、次のことを知っておく必要があります

  • クラスローディングとは何ですか?
  • JVMがクラスをロードする方法
  • ClassNotFound が発生するのはなぜですか?

Tomcat が Web アプリケーションでサーブレットをロードして管理する方法について考えてみましょう。
Tomcat は Context コンポーネントを通じて Web アプリケーションをロードおよび管理するため、今日は Tomcat のクラス ロード メカニズムを詳細に分析します。しかし、その前に、JVM クラス ローディング メカニズムをプレビューする必要があります。まず、冒頭で提起された質問に答え、次に Tomcat のクラス ローダーが Java の親委任メカニズムをどのように破壊するかについて説明します。

JVM クラスローダー

Java クラスのロードとは、バイトコード形式の .class ファイルを JVM のメソッド領域にロードし、JVM ヒープ内に java.lang.Class オブジェクト インスタンスを作成して、Java クラスに関連するデータとメソッドをカプセル化することです。

クラスオブジェクトとは何ですか?
これはビジネス クラスのテンプレートとして理解でき、JVM はテンプレートに基づいて特定のビジネス クラス オブジェクト インスタンスを作成します。

JVM は起動時にすべての .class ファイルをロードするのではなく、プログラムが動作中に使用されるときにのみクラスをロードします。
JVM クラスのロードはクラス ローダーによって行われます。JDK は抽象クラス ClassLoader を提供します。

パブリック抽象クラス ClassLoader {

    //各クラスローダーには親ローダーがあります private final ClassLoader parent;
    
    パブリッククラス<?> loadClass(文字列名) {
  
        // クラスがロードされているかどうかを確認します Class<?> c = findLoadedClass(name);
        
        // ロードされていない場合 if( c == null ){
          // [再帰] 親ローダーにロードを委任する if (parent != null) {
              クラス名をロードします。
          } それ以外 {
              // 親ローダーが空の場合は、Bootstrap ローダーがロードされているかどうかを確認します c = findBootstrapClassOrNull(name);
          }
        }
        // 親ローダーがロードに失敗した場合は、独自のfindClassを呼び出してロードします。if (c == null) {
            クラス名を検索します。
        }
        
        c を返します。
    }
    
    保護されたクラス<?> findClass(文字列名){
       // 1. 渡されたクラス名に従って、特定のディレクトリ内のクラス ファイルを検索し、.class ファイルをメモリに読み込みます...
          
       // 2. defineClass を呼び出してバイト配列を Class オブジェクトに変換します。 return defineClass(buf, off, len);
    }
    
    // バイトコード配列をクラスオブジェクトに解析し、ネイティブメソッドで実装します。protected final Class<?> defineClass(byte[] b, int off, int len){
       ...
    }
}

JVM のクラス ローダーは階層的な親子関係を持ち、各クラス ローダーは親ローダーを指す親フィールドを保持します。

  • defineClassツールメソッド: ネイティブメソッドを呼び出して、JavaクラスのバイトコードをClassオブジェクトに解析します。
  • findClassは、ファイルシステムまたはネットワークから取得される可能性のある.classファイルを検索します。検索後、.classファイルをメモリに読み込み、バイトコード配列を取得し、defineClassメソッドを呼び出してClassオブジェクトを取得します。

loadClass は、まずクラスがロードされているかどうかを確認します。ロードされている場合は直接戻り、ロードされていない場合は親ローダーに渡してロードします。
これは再帰呼び出しです。つまり、子ローダーは親ローダーへの参照を保持します。クラスローダーが Java クラスをロードする必要がある場合、最初にロードを親ローダーに委任し、次に親ローダーは自身のロードパスで Java クラスを検索します。親ローダーが自身のロード範囲内で Java クラスを見つけられない場合は、ロードのために子ローダーに返します。これが親委任メカニズムです。

JDK クラス ローダーの動作原理は同じですが、唯一の違いはロード パス、つまり findClass によって検索されるパスが異なります。
親委任メカニズムは、JVM 内の Java クラスの一意性を保証するためのものです。誤って Object などの JRE コア クラスと同じ名前のクラスを記述した場合、親委任メカニズムにより、記述した Object ではなく、JRE 内の Object クラスがロードされることが保証されます。
AppClassLoader が Object クラスをロードするときに、ロードを ExtClassLoader に委任し、ExtClassLoader は BootstrapClassLoader に委任します。BootstrapClassLoader は Object クラスがすでにロードされていることを検出し、Object クラスをロードせずに直接戻ります。

クラス ローダーの親子関係は継承を通じて実装されません。たとえば、AppClassLoader は ExtClassLoader のサブクラスではありませんが、AppClassLoader の親は ExtClassLoader オブジェクトを指します。
したがって、クラス ローダーをカスタマイズする場合は、AppClassLoader を継承する代わりに、ClassLoader 抽象クラスを継承して、findClass と loadClass を書き換えることができます。
Tomcat は、カスタム クラス ローダーを通じて独自のクラス ローディングを実装します。
親委任を解除したい場合は、loadClass を書き換えるだけで済みます。これは、loadClass のデフォルトの実装が親委任メカニズムであるためです。

Tomcat クラスローダー

Tomcat のカスタム クラス ローダー WebAppClassLoader は親の委任メカニズムを破壊します。
まず、自分でクラスのロードを試みます。見つからない場合は、親クラスローダーに委譲します。目的は、Web アプリケーション自体で定義されたクラスのロードを優先することです。
ClassLoader の 2 つのメソッドを書き換えるだけです。

クラスを検索

パブリック Class<?> findClass(String name) は ClassNotFoundException をスローします {
    ...
    
    クラス<?> clazz = null;
    試す {
            //1. まず、Web アプリケーション ディレクトリでクラスを検索します。clazz = findClassInternal(name);
    } キャッチ (RuntimeException e) {
           eを投げる;
       }
    
    (clazz == null)の場合{
    試す {
            //2. ローカル ディレクトリで見つからない場合は、親ローダーで検索させます。clazz = super.findClass(name);
    } キャッチ (RuntimeException e) {
           eを投げる;
       }
    
    //3. 親クラスが見つからない場合は、ClassNotFoundException をスローします。
    (clazz == null)の場合{
        新しい ClassNotFoundException(名前) をスローします。
     }

    戻りクラッズ;
}

ワークフロー

  • まず、Webアプリケーションのローカルディレクトリにロードするクラスを検索します。
  • 見つからない場合は、親ローダー、つまりAppClassLoaderに渡されます。
  • 親ローダーもクラスを見つけられない場合は、ClassNotFoundをスローします。

ロードクラス

パブリック Class<?> loadClass(String name, boolean resolve) は ClassNotFoundException をスローします {

    同期化 (getClassLoadingLock(名前)) {
 
        クラス<?> clazz = null;

        //1. まず、ローカル キャッシュでクラスがロードされているかどうかを確認します。clazz = findLoadedClass0(name);
        (clazz != null)の場合{
            もし(解決する)
                クラスを解決します(clazz);
            戻りクラッズ;
        }

        //2. システム クラス ローダーがロードされているかどうかを確認します clazz = findLoadedClass(name);
        (clazz != null)の場合{
            もし(解決する)
                クラスを解決します(clazz);
            戻りクラッズ;
        }

        // 3. ExtClassLoader クラス ローダーを使用してクラスをロードしようとしますが、なぜでしょうか?
        クラスローダー javaseLoader = getJavaseClassLoader();
        試す {
            clazz = javaseLoader.loadClass(名前);
            (clazz != null)の場合{
                もし(解決する)
                    クラスを解決します(clazz);
                戻りクラッズ;
            }
        } キャッチ (ClassNotFoundException e) {
            // 無視する
        }

        // 4. ローカルディレクトリでクラスを検索してロードしてみる try {
            clazz = findClass(名前);
            (clazz != null)の場合{
                もし(解決する)
                    クラスを解決します(clazz);
                戻りクラッズ;
            }
        } キャッチ (ClassNotFoundException e) {
            // 無視する
        }

        // 5. システムクラスローダー(AppClassLoader)を使用してロードを試みる try {
                clazz = Class.forName(名前、false、親);
                (clazz != null)の場合{
                    もし(解決する)
                        クラスを解決します(clazz);
                    戻りクラッズ;
                }
            } キャッチ (ClassNotFoundException e) {
                // 無視する
            }
       }
    
    //6. 上記のプロセスはすべてロードに失敗し、例外をスローします。throw new ClassNotFoundException(name);
}

ワークフロー

  • まずローカルキャッシュをチェックしてクラスがロードされているかどうかを確認します
  • つまり、Tomcat のクラス ローダーがこのクラスをすでにロードしているかどうかです。
  • Tomcat クラス ローダーがクラスをロードしていない場合は、システム クラス ローダーがクラスをロードしたかどうかを確認します。
  • いずれも存在しない場合は、ExtClassLoader にそれをロードさせて、Web アプリケーション独自のクラスが JRE コア クラスを上書きしないようにします。
  • Tomcat は親の委任を解除する必要があるため、Web アプリケーションで Object というクラスをカスタマイズする場合、Object クラスが最初にロードされると、JRE Object クラスが上書きされます。そのため、Tomcat クラス ローダーは最初に ExtClassLoader を使用してロードしようとします。これは、ExtClassLoader が BootstrapClassLoader にロードを委任するためです。BootstrapClassLoader は Object クラスがすでにロードされていることを検出し、それを Tomcat クラス ローダーに直接返します。このようにして、Tomcat クラス ローダーは Web アプリケーションの下で Object クラスをロードせず、JRE コア クラスの上書きを回避します。
  • ExtClassLoader がロードに失敗した場合、つまり JRE にこのクラスがない場合は、ローカル Web アプリケーション ディレクトリで検索してロードします。
  • ローカル ディレクトリにそのようなクラスが存在しない場合は、Web アプリケーション自体で定義されたクラスではないことを意味し、システム クラス ローダーによってロードされます。ここで、Class.forName のデフォルト ローダーがシステム クラス ローダーであるため、Web アプリケーションは Class.forName 呼び出しを通じてシステム クラス ローダーに渡されることに注意してください。
  • 上記の読み込みプロセスが失敗した場合は、ClassNotFoundをスローします。

Tomcat クラス ローダーは親の委任を破棄し、最初は親ローダーに直接委任せず、最初にローカル ディレクトリにロードすることがわかります。
ただし、ローカル ディレクトリ クラスが JRE コア クラスを上書きするのを防ぐために、最初に ExtClassLoader を使用してそれらをロードします。
では、まず AppClassLoader でロードしてみませんか?
この場合、再び親委任となり、これが Tomcat クラス ローダーの謎となります。

Tomcat が親委任メカニズムを破壊する仕組みについての記事はこれで終わりです。Tomcat の親委任メカニズムの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Tomcat プロセスの CPU 使用率が高い場合の解決策
  • SpringBootは組み込みTomcat実装手順を開始します
  • Tomcat は親の委任メカニズムを破壊して Web アプリケーションの分離を実現します。
  • Tomcatを使用して共有ライブラリを設定し、同じjarを共有する
  • Tomcat の面接の質問が 15 問も出る、滅多にない機会です!

<<:  時点別のMySQLデータベース復旧実績

>>:  CSS のサイズと幅と高さのブラウザ解釈の違いに対する解決策

推薦する

Mysql sql スロークエリ監視スクリプトコード例

1. my.cnfを変更する #全体的な効果としては、グローバルがオンになっている場合はテーブルとロ...

MySQL データ型の最適化の原則

MySQL は多くのデータ型をサポートしており、高パフォーマンスを得るには適切なデータ型を選択するこ...

Docker で複数のアプリケーション サイトをプロキシするために Nginx を使用する方法

序文エージェントの役割は何ですか? - 複数のドメイン名が同じサーバーに解決される- 1つのサーバー...

Vue でインデックスをキー属性値として使用することが推奨されないのはなぜですか?

目次序文キーの役割差分アルゴリズムにおけるキーの役割ヘッドノードを同期するテールノードを同期する新し...

数字当てゲームを実装するための純粋なJavaScript

100 以内の自然数をランダムに選択し、プレイヤーに 10 ラウンド以内にその数を推測させる数字推...

Vue カスタム オプション時間カレンダー コンポーネント

この記事の例では、参考のためにvueカスタムオプションタイムカレンダーコンポーネントの具体的なコード...

テーブルを使用する場合と CSS を使用する場合 (経験の共有)

TW のメインテキスト ページは、以前は小さなモニターと低解像度のユーザーを考慮して幅が 850 ピ...

Vue イメージ ドラッグ アンド ドロップ ズーム コンポーネントの使用方法の詳細な説明

Vueイメージドラッグアンドドロップズームコンポーネントの具体的な使い方は参考までに。具体的な内容は...

ウェブ上でチャートを描くための 9 つの優れた JavaScript フレームワーク スクリプト

ウェブ上でチャートを描くための 9 つの優れた JavaScript フレームワーク スクリプト 1...

MySQLのメモリ使用量を表示する方法の詳細な説明

序文この記事では主にMySQLのメモリ使用量に関する関連コンテンツを紹介し、皆さんの参考と学習のため...

JS の new 関数の詳細な説明

目次1. 例2. 兵士100人を作成する3. 質問4. 改善点5. エレガント? 6. JSの父から...

Vue のスロットスコープの詳細な理解(初心者向け)

Baidu には slot-scope に関する記事が既にたくさんありますが、以前よく学習しておら...

MySQL における explain の役割の詳細な説明

1. MYSQLインデックスインデックス: MySQL がデータを効率的に取得するのに役立つデータ構...

Nginx でファイル ホットリンク保護サービスを構築する方法を学ぶ例

序文多くのサイトが、ポイントやゴールドコインなど、情報のダウンロードに料金を請求していることは誰もが...

シンプルなタブバー切り替えケースを実現するJavaScript

この記事では、タブバーの切り替え効果を簡単に実現するためのJavaScriptの具体的なコードを参考...