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 のサイズと幅と高さのブラウザ解釈の違いに対する解決策

推薦する

div間のギャップの解決策

HTMLのdivブロックを使用していて、ブロックの中央をしっかりと接続できず、解決できない場合1. ...

Windows 上の MySQL 5.6 で my.ini 設定ファイルを見つける方法

忘れてしまった場合に後で戻って確認できるようにメモしておいてください。問題の説明:今日はちょっとした...

Vue ルーター vue-router 詳細説明ガイド

中国語ドキュメント: https://router.vuejs.org/zh/ Vue Router...

美容・スタイリングウェブサイト向けのカラーマッチングテクニックと効果表示

色はあらゆるウェブサイトにとって最も重要な要素の 1 つであり、閲覧者に大きな影響を与えるため、色の...

LeetCode の SQL 実装 (184. 部門内で最も高い給与)

[LeetCode] 184. 部門最高給与従業員テーブルにはすべての従業員が保存されます。すべて...

Nginx に React プロジェクトをデプロイする方法の例

テストプロジェクト: react-demo react-demo プロジェクトをサーバーにクローンし...

MySQLの再帰問題

MySQL自体は再帰構文をサポートしていませんが、自己接続を通じていくつかの単純な再帰を実現できます...

Nginx で 403 forbidden を解決するための完全な手順

ウェブページに403 Forbiddenと表示されるNginx (yum インストール ログは通常 ...

JavaScriptはPromiseを使用して複数の繰り返しリクエストを処理します

1. なぜこの記事を書くのですか?重複リクエストの処理に関する記事をたくさん読んだことがあるでしょう...

Vueルーティングコンポーネントでパラメータを渡す8つの方法の詳細な説明

シングルページアプリケーションを開発する場合、特定のルートを入力し、パラメータに基づいてサーバーから...

Nginxリバースプロキシ設定でプレフィックスが削除される

nginx をリバース プロキシとして使用する場合、リクエストをそのまま次のサービスに転送するだけで...

Centos6.5 で MySQL 5.7.19 をインストールして設定する方法

Centos6.5にmysql5.7.19をインストールするための詳細な手順は次のとおりです。 1....

MySQLにおけるテーブルインデックスの定義方法と導入

概要インデックスは、テーブル内の 1 つ以上の列に基づいて DBMS によって特定の順序で作成される...

LinuxのCentos7でmysql5.7.29を構築する詳細なプロセス

1. MySQLをダウンロードする1.1 ダウンロードアドレスhttps://downloads.m...

MySql バッチ挿入の最適化 SQL 実行効率の例の詳細な説明

MySql バッチ挿入の最適化 SQL 実行効率の例の詳細な説明itemcontractprice ...