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 を使用してデータ分析を実行するために、大量のデータをデータベースにインポー...

JavaScript の静的スコープと動的スコープを例を使って説明します

目次序文静的スコープと動的スコープ静的スコープ実行プロセス動的スコープ実行プロセスエクササイズ練習1...

Vue でルートをジャンプする方法をご存知ですか?

目次最初の方法: router-link (宣言型ルーティング) 2番目の方法: router.pu...

Vue+video.jsはビデオプレイリストを実装します

この記事では、ビデオプレイリストを実装するためのvue + video.jsの具体的なコードを参考ま...

ページ切り替え効果を実現するJSコード

この記事の例では、ページ切り替え効果を実現するためのJSコードの具体的なコードを参考までに共有してい...

MySQL 重複インデックスと冗長インデックスの例の分析

この記事では、例を使用して MySQL の重複インデックスと冗長インデックスについて説明します。ご参...

一般的な nginx コマンドをシェル スクリプトに組み込む方法の詳細な説明

1. nginxシェルスクリプトを保存するフォルダを作成する /usr/local/タスク/ngin...

検索データ表示を実装するJavaScript

この記事ではJavaScript検索のデータ表示コードを参考までに共有します。具体的な内容は以下のと...

Google Recaptcha 認証を使用した Vue 実装例

最近のプロジェクトでは、Google ロボット認証を使用する必要があります。これには VPN が必要...

MySQL 5.7.18 MSI インストール グラフィック チュートリアル

この記事では、参考までにMySQL 5.7.18 MSIインストールチュートリアルを紹介します。具体...

MYSQLは、ショッピングカートに追加する際に重複追加を防ぐためのサンプルコードを実装します。

序文最近、仕事の都合で、APP ショッピングカートの注文支払いに取り組んでいました。テスト中にバグが...

JavaScript プリミティブデータ型シンボルの詳細な説明

目次導入説明名前の競合私有財産要約する導入シンボル変数を作成する最も簡単な方法は、Symbol() ...

MySQLデータベースのスケジュールバックアップを実装する方法

1. シェルスクリプトを作成する vim バックアップdb.sh 次のようにスクリプトを作成します。...

Linux lessコマンド例の詳細な説明

ファイル名が少ないファイルを表示ファイル名を少なく | grep -n コンテンツを検索内容に応じて...

Mysql ALTER TABLE はフィールドを追加するときにテーブルをロックしますか?

目次MySQL 5.6以前MySQL 5.6以降要約する知らせMySQL 5.6以前更新手順元のテー...