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 クロスブラウザ スタイルのバグのデバッグについて

推薦する

HTML マルチヘッダーテーブルコード

1. マルチヘッダーテーブルコードコードをコピーコードは次のとおりです。 <!DOCTYPE ...

Linux での MySQL 5.7.19 (tar.gz) インストール グラフィック チュートリアル

Linux で MySQL-5.7.19 バージョンをインストールするための最初のチュートリアル。す...

Vue ページ状態の永続化の詳細な説明

目次コード:補充:要約する要件: 左のツリーと右のテーブル。組織ツリーでノードを選択した後、詳細ペー...

XHTML の珍しいが便利なタグ

Xhtml には、あまり使用されないが非常に便利なタグが多数あります。半分の労力で 2 倍の結果を達...

最も単純な ErrorBoundary コンポーネントをカプセル化して、React 例外を処理する

序文React 16から、子コンポーネントで発生したエラーを捕捉し、エラーログを記録し、ダウングレー...

Vue.jsはElement-uiを使用してナビゲーションメニューを実装します

この記事では、Element-uiを使用してvue.jsでナビゲーションメニューを実装するための具体...

MySQL のロングトランザクション例の詳細な説明

序文: 「MySQL 入門」シリーズの記事は終了しました。今後も引き続き MySQL に焦点を当て、...

CSS マージンの重複とその防止方法

2 つ以上のブロックレベル ボックスの垂直に隣接するエッジが重なり合っています。結果として得られる境...

CSS はスクロールバーを非表示にしてコンテンツをスクロールする効果を実現します (3 つの方法)

フロントエンド開発では、スクロールバーを非表示にしながらスクロールをサポートしなければならないという...

Vueは動的ルーティングの詳細を実装します

目次1. フロントエンド制御1. router.js ファイル内 (router.js にそれぞれ静...

ミニプログラムは、カスタムのマルチレベル単一選択と複数選択を実装します

この記事では、参考のために、ミニプログラムでカスタムのマルチレベル単一選択および複数選択機能を実装す...

VirtualBox の仮想ディスク vdi ファイルの容量を拡張する方法 (グラフィック チュートリアル)

VirtualBoxのインストールディレクトリを見つけます。ディレクトリ内には容量を拡張するために...

CocosCreator 入門チュートリアル: ネットワーク通信

ネットワーク通信の概要オンライン ゲームを開発する場合、必然的にネットワーク通信に対処する必要があり...

MySQL公式パフォーマンステストツールmysqlslapの使い方の紹介

目次導入説明書実際の経験まとめ導入MySQL は最も人気のあるオープンソース データベースとして、さ...

MYSQLについては、データ型と操作テーブルを知る必要があります

データ型と操作データテーブル1.1 MySQL 型: 整数 1.2 MySQL データ型: 浮動小数...