Tomcatのクラスロードメカニズムのプロセスとソースコード分析

Tomcatのクラスロードメカニズムのプロセスとソースコード分析

序文

前回の「Java 仮想マシン: オブジェクト作成プロセスとクラス読み込みメカニズム、親委任モデル」の記事では、JVM クラス読み込みメカニズムと親委任モデルについて紹介しました。親委任モデルのクラス読み込みプロセスは、主に次の手順に分かれています。

(1)ClassLoaderを初期化するときに、親が誰であるかを指定する必要があります

(2)まずクラスがロードされているかどうかをチェックする。クラスがロードされている場合は直接

(3)ロードされていない場合は、親ローダーのloadClass()メソッドを呼び出してロードする

(4)親ローダーが空の場合、デフォルトでブートストラップクラスローダーが使用され、

(5) 親クラスのロードに失敗した場合は、ClassNotFoundExceptionがスローされ、findClass()メソッドが呼び出されてロードされます。

前の記事で述べたように、このメカニズムを破壊したい場合は、クラスローダー(ClassLoader から継承)をカスタマイズし、その中の loadClass() メソッドを書き換えて、親の委譲を実行しないようにすることができます。最も典型的な例は、Tomcat コンテナのクラス ローディング メカニズムです。これは、独自のクラス ローダー WebApp ClassLoader を実装し、親の委任モデルを破壊します。各アプリケーションがデプロイされると、一意のクラス ローダーが作成されます。

1. Tomcat クラスローダー構造図:

(1) Common ClassLoader: common.loaderプロパティの下のjarをロードします。通常はCATALINA_HOME/libディレクトリにあり、主にTomcatやアプリケーションの一般的なクラスで使用されます。

(2) Catalina ClassLoader: server.loader プロパティの下にある jar をロードします。デフォルトではパスは設定されておらず、親ローダーである Common ClassLoader を返します。主にサーバー内の表示可能なクラスをロードします。これらのクラスにはアプリケーションからアクセスできません。

(3) 共有クラスローダー: share.loader 属性の下にある jar をロードします。デフォルトではパスは設定されておらず、親ローダーである Common ClassLoader を返します。これは主にアプリケーションの共有クラスをロードするために使用されます。これらのクラスは Tomcat 自体には表示されません。

tomcat/conf/catalina.properties 構成ファイルで server.loader および share.loader 項目を指定した後にのみ、Catalina ClassLoader および Shared ClassLoader のインスタンスが実際に作成されます。それ以外の場合は、これら 2 つのクラス ローダーの代わりに Common ClassLoader のインスタンスが使用されます。ただし、これら 2 つのローダー項目は、デフォルトの構成ファイルでは設定されていません。

(4) WebApp ClassLoader: Tomcat は複数の WebApp ClassLoader インスタンスを持つことができます。各アプリケーションには、アプリケーションの /WEB-INF/classes および /WEB-INF/lib の下のクラスをロードするための固有の WebApp ClassLoader があります。

2. Tomcat のクラスロードプロセスの説明:

Tomcat がクラスのロードに WebAppClassLoader を使用する場合、具体的なプロセスは次のようになります。

(1) まず、クラスがローカルキャッシュにロードされているかどうかを確認し、Tomcat がクラスをロードしたかどうかを確認します。

(2)Tomcatがこのクラスをロードしていない場合、システムクラスローダーのキャッシュをチェックしてロードされているかどうかを確認します。

(3) そうでない場合は、ExtClassLoader クラスローダーを使用してクラスをロードします。ここでポイントになります。Tomcat の WebAppClassLoader は、最初に AppClassLoader を使用してクラスをロードするのではなく、直接 ExtClassLoader を使用してクラスをロードします。ただし、ExtClassLoader は依然として親の委任に従います。Bootstrap ClassLoader を使用してクラスをロードし、Jre のコア クラスが繰り返しロードされないようにします。

たとえば、Web で Object クラスをロードします。 WebAppClassLoader → ExtClassLoader → Bootstrap ClassLoader、この読み込みチェーンにより、オブジェクトが繰り返し読み込まれないことが保証されます。

(4) 読み込みが成功しなかった場合、WebAppClassLoader は独自の findClass() メソッドを呼び出してクラスを自動的に読み込み、最初に WEB-INF/classes から読み込み、次に WEB-INF/lib から読み込みます。

(5) それでも読み込みが成功しない場合は、WebAppclassLoader は SharedClassLoader に委任し、SharedClassLoader は CommonClassLoader に委任し、CommonClassLoader は AppClassLoader に委任し、最終的に BootstrapClassLoader に委任されて、クラスが独自のディレクトリにレイヤーごとにロードされます。

(6)いずれのファイルも正常に読み込まれなかった場合は例外がスローされます。

3. ソースコード分析:

(1) WebAppClassLoaderのloadClass()メソッドのソースコード:

WebappClassLoader アプリケーション クラス ローダー loadClass は、親クラス WebappClassLoaderBase にあります。

パブリック 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 クラスローダーを使用してクラスをロードします (ExtClassLoader は親の委任に従い、ExtClassLoader は Bootstrap ClassLoader を使用してクラスをロードします)
        クラスローダー 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);
}

(2) WebAppClassLoaderのfindClass()メソッドのソースコード:

パブリック Class<?> findClass(String name) は ClassNotFoundException をスローします {
    // 可能であれば、スーパークラスにこのクラスを見つけるように依頼します
    // (見つからない場合は ClassNotFoundException をスローします)
    クラス<?> clazz = null;
 
    // まず、自分のWebアプリケーションディレクトリでクラスを検索します
    clazz = findClassInternal(名前);
 
    // 見つからない場合は親クラスで処理します if ((clazz == null) && hasExternalRepositories) {  
        clazz = super.findClass(クラス名);
    }
    (clazz == null)の場合{
         新しい ClassNotFoundException(名前) をスローします。
    }
    戻りクラッズ;
}

4. Tomcat が独自のクラスロードメカニズムを実装するのはなぜですか?

WebAppClassLoader はクラスをロードするときに、JVM 親委任メカニズムを意図的に破壊し、AppClassLoader をバイパスして、ExtClassLoader を直接使用してクラスをロードします。主な理由は、同じ Web コンテナーにデプロイされた異なる Web アプリケーションで使用されるクラス ライブラリを相互に分離して、異なるプロジェクト間の相互影響を回避するためです。もちろん、他にも次のような理由があります。

(1) Webコンテナ自体のセキュリティがデプロイされたWebアプリケーションの影響を受けないようにするため、Tomcatが使用するクラスライブラリは、デプロイされたアプリケーションのクラスライブラリから独立している必要があります。

(2)いくつかの基本クラスが同時にロードされないようにする。いくつかのクラスライブラリは、サーブレットAPIなど、Tomcatとデプロイされたアプリケーション間で共有できます。

(3)同じWebコンテナにデプロイされたアプリケーション間でクラスライブラリを共有できるようにします。これは主な理由が矛盾しているように聞こえますが、実は非常に合理的です。クラスはクラスローダーによって仮想マシンにロードされた後、メソッド領域の永続世代に保存されます。クラスライブラリを共有できない場合、仮想マシンのメソッド領域が過度に拡張されるリスクに簡単にさらされます。たとえば、多数のアプリケーションを Spring で管理していて、Spring クラス ライブラリを共有できない場合、各アプリケーションの Spring クラス ライブラリが 1 回ずつロードされることになり、リソースが大量に無駄になります。

概要: Tomcat では、WebAppClassLoader ローダーのみが親の委任を破棄しますが、他のクラス ローダーは引き続き親の委任に従います。 これを行う主な理由は、同じ Web コンテナー内の異なる Web アプリケーションで使用されるクラス ライブラリが相互干渉を避けるために互いに独立していることを保証するためです。

参考記事: https://www.jb51.net/article/229561.htm

Tomcat のクラスローディングメカニズムのプロセスとソースコード分析に関するこの記事はこれで終わりです。Tomcat のクラスローディングメカニズムに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Tomcat クラスローダーの実装方法とサンプルコード

<<:  MySql データベースにおける単一テーブル クエリと複数テーブル結合クエリの効率の比較

>>:  CSS クロスブラウザ スタイルのバグのデバッグについて

推薦する

シェル スクリプトを使用してワンクリックで MySQL 5.7.29 をインストールする方法

この記事は51CTOブログの著者wjw555の作品を参照しています。スクリプトの内容: vim イン...

MySQL 検査スクリプト (必読)

以下のように表示されます。 #!/usr/bin/env python3.5 psutilをインポー...

Vueはシンプルなショッピングカートの例を実装します

この記事では、参考までに、Vue の具体的なコードを共有して、簡単なショッピングカートを実装します。...

docker デプロイメントの実装手順 lnmp-wordpress

目次1. 実験環境2. Dockerソースをインストールする3. Dockerをインストールする4....

XHTML 1.0 リファレンス

機能別に並べ替えNN: このタグをサポートする Netscape の以前のバージョンを示しますIE:...

MySQL の lru リンク リストの簡単な分析

1. 従来のLRUリンクリストについて簡単に説明するLRU:最も最近使われなかったものLRU リンク...

jQuery Ajax チャットボットの実装事例

チャットボットは多くの手作業を省くことができ、顧客サービス、天気予報対応など、さまざまな状況で使用で...

MySQL における 10 進数型の使用法の簡単な紹介

MySQL でサポートされている浮動小数点型は、 FLOAT 、 DOUBLE 、およびDECIMA...

Docker を使って LEMP 環境を素早く構築する方法の例

LEMP(Linux + Nginx + MySQL + PHP)は、基本的に今日のWeb開発者にと...

CSS のインライン スタイルに変換するソリューション (css-inline)

シーンについて話すメールを送信サードパーティのウェブサイトにHTMLを埋め込む他の編集者の記事をコピ...

Dockerはポートマッピングを設定しますが、ソリューションにアクセスできません

#docker ps チェック、すべてのポートがマップされています コンテナID イメージ コマンド...

Alibaba Cloud ホストが IP を使用して Web サイトにアクセスできない問題の解決策 (セキュリティ グループ ルールを構成することで解決)

Alibaba Cloud ホストを購入したばかりで、その速度を試すのが待ちきれませんでした。しか...

CSSを使用してHTMLテキストボックス内のテキストの垂直方向の中央を制御する

Text の height 属性が定義されている場合、Text に入力されたテキストは垂直方向に中央...