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 <> および <=> 演算子の紹介

<> 演算子機能: 等しくないことを示します。注: 「!=」演算子と同じ機能を持ちますが...

jQueryはショッピングカートの完全な機能を実現します

この記事では、ショッピングカートの完全な機能を実現するためのjQueryの具体的なコードを参考までに...

MySQL における楽観的ロックと悲観的ロックの例

データベース管理システムにおける同時実行制御のタスクは、データベース内の同じデータに同時にアクセスす...

Javascript実践におけるコマンドモードの詳しい説明

目次意味構造例カスタムショートカットキー元に戻すとやり直し録音と再生マクロ要約する意味リクエストをオ...

JavaScriptのアンチシェイクとスロットリングとは

目次1. 関数デバウンス1. 画像安定化とは何ですか? 2. 関数のスロットリング2.1 タイマーの...

MySQL テーブルを返すとインデックスが無効になるケースの説明

導入MySQL InnoDB エンジンがレコードをクエリし、インデックス カバレッジを使用できない場...

Centos7 システムに k8s クラスターを展開するための詳細な紹介

目次1 バージョンと計画1.1 バージョン情報: 1.2 クラスター計画2. 展開1. ファイアウォ...

js 配列エントリ() 反復メソッドを取得する

目次1.entires() メソッドの詳細な構文2.entires() メソッドの一般的な使用法と注...

Nginxでネットワーク分離を解決した実践記録を詳しく解説

必要最近、Node オンライン サービスを移行する必要があったため、2 つの新しいオンライン サーバ...

WeChatアプレットを少なく使う方法(最適な方法)

序文私は less/sass を書くことに慣れていますが、小さなプログラムを開発するときには、まだ ...

数十億のデータに対するMySQLページングの最適化に関する簡単な説明

目次背景分析するデータシミュレーション1. 従業員テーブルと部門テーブルの2つのテーブルを作成します...

CentOS 7 での mysql 5.7 のインストール チュートリアル

1. 公式MySQL Yumリポジトリをダウンロードしてインストールする 実行ファイル: mysql...

vite2.0 設定学習の詳しい説明(typescript 版)

導入悠宇希の原文です。 vite は Vue CLI に似ています。vite も、基本的なプロジェク...

WeChatミニプログラムの基本チュートリアル:Echartの使用

序文まずは最終的な効果を見てみましょう。私が自分で作った小さなデモです。まずEChartsの公式サイ...

MySQLのイベントスケジューラEVENTを理解する

MySQL のイベント スケジューラ EVENT は、Unix crontab や Windows ...