- 序文 -Apache Tomcat のクラスローディングメカニズムを理解していますか?この記事では、基礎となる原則から始めて、Tomcat クラスのロードに関係するソース コード、メカニズム、ソリューションを徹底的に明らかにし、Tomcat クラスのロードの核心を深く理解できるようにします。 - JVM クラスローダー -1. JVMクラスローダーTomcat クラス ローダーについて言えば、次の図に示すように、JVM クラス ローダーについても簡単に説明する必要があります。
これらのクラス ローダーの動作原理は同じですが、違いはロード パスが異なること、つまり findClass メソッドによって検索されるパスが異なることです。 親委任メカニズムは、Java クラスが JVM 内で一意であることを保証するものです。Object クラスなど、JRE コア クラスと同じ名前のクラスを誤って記述した場合、親委任メカニズムにより、記述した Object クラスではなく、JRE 内の Object クラスがロードされることが保証されます。 これは、AppClassLoader が Object クラスをロードするときに、ロードを ExtClassLoader に委任し、ExtClassLoader が BootstrapClassLoader に委任するためです。BootstrapClassLoader は Object クラスがすでにロードされていることを検出し、作成した Object クラスをロードせずに直接戻ります。 ここで、クラス ローダーの親子関係は継承を通じて実装されないことに注意してください。たとえば、AppClassLoader は ExtClassLoader のサブクラスではありませんが、AppClassLoader の親メンバー変数は ExtClassLoader オブジェクトを指します。同様に、クラス ローダーをカスタマイズする場合は、AppClassLoader を継承するのではなく、ClassLoader 抽象クラスを継承し、findClass メソッドと loadClass メソッドを書き換えます。Tomcat は、カスタム クラス ローダーを通じて独自のクラス ローディング ロジックを実装します。親委任メカニズムを破壊したい場合は、loadClass メソッドを書き直す必要があることに気付いたかどうかわかりません。これは、loadClass のデフォルトの実装が親委任メカニズムであるためです。 2. クラスローダーのソースコードパブリック抽象クラス ClassLoader { // 各クラスローダーには親ローダーがあります private final ClassLoader parent; パブリック Class<?> loadClass(String name) は ClassNotFoundException をスローします { loadClass(name, false) を返します。 } 保護されたクラス<?> loadClass(文字列名、ブール値解決) ClassNotFoundException をスローします { // まず、クラスがすでにロードされているかどうかを確認します クラス<?> c = findLoadedClass(名前); // ロードされていない場合 if (c == null) { 親が null ではない場合 // 最初に親ローダーにロードを委任します。これは再帰呼び出しであることに注意してください。c = parent.loadClass(name, false); } それ以外 { // 親ローダーが空の場合は、Bootstrap ローダーがロードされているかどうかを確認します c = findBootstrapClassOrNull(name); } // 親ローダーがロードに失敗した場合は、独自のfindClassを呼び出してロードします。if (c == null) { クラス名を検索します。 } } c を返します。 } } //ClassLoader の findClass メソッドはサブクラスでオーバーライドする必要があります。次のコードは対応するコードです。protected Class<?> findClass(String name){ //1. 渡されたクラス名に従って、特定のディレクトリ内のクラス ファイルを検索し、.class ファイルをメモリに読み込みます... //2. バイト配列を Class オブジェクトに変換するには、defineClass を呼び出します。 return defineClass(buf, off, len); } // バイトコード配列をクラスオブジェクトに解析し、ネイティブメソッドで実装します。protected final Class<?> defineClass(byte[] b, int off, int len){ } } カスタム クラス ローダーでは、ClassLoader の loadClass メソッドを書き換える必要があります。 - Tomcatのクラスロードメカニズム - 1. 荷重機構の特徴分離: Web アプリケーション ライブラリは相互に分離されており、依存するライブラリやアプリケーション パッケージが相互に影響を与えないようにします。 2 つの Web アプリケーションがあり、1 つは Spring 2.5 を使用し、もう 1 つは Spring 4.0 を使用し、アプリケーション サーバーが 1 つのクラス ローダーを使用してロードする場合、Jar パッケージの上書きが原因で Web アプリケーションが正常に起動しなくなると想像してください。 柔軟性: Web アプリケーション間のクラス ローダーは互いに独立しているため、1 つの Web アプリケーションのみを再デプロイでき、その Web アプリケーションのクラス ローダーは他の Web アプリケーションに影響を与えずに再作成されます。クラス ローダーを使用する場合、これは明らかに不可能です。クラス ローダーが 1 つしかない場合、クラス間の依存関係が整理されず、Web アプリケーションのクラスを完全に削除することは不可能だからです。 パフォーマンス: 各 Web アプリケーションにはクラス ローダーがあるため、Web アプリケーションはクラスをロードするときに他の Web アプリケーションに含まれる Jar パッケージを検索しません。アプリケーション サーバーにクラス ローダーが 1 つしかない場合よりも、パフォーマンスは当然高くなります。 2. Tomcatクラスローディングソリューション
Tomcat 8.5 では、デフォルトで厳密な親委任メカニズムが変更されました。
3. アプリケーションクラスローダーの読み込みプロセスを分析するアプリケーション クラス ローダーは WebappClassLoader であり、その loadClass は親クラス WebappClassLoaderBase にあります。 パブリック Class<?> loadClass(String name, boolean resolve) は ClassNotFoundException をスローします { 同期化 (getClassLoadingLock(名前)) { (log.isDebugEnabled())の場合 log.debug("loadClass(" + name + ", " + resolution + ")"); クラス<?> clazz = null; // 停止したクラスローダーへのアクセスをログに記録する checkStateForClassLoading(名前); //現在の ClassLoader のローカル キャッシュからクラスをロードし、見つかった場合は返します。clazz = findLoadedClass0(name); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("キャッシュからクラスを返す"); もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } // ローカル キャッシュがない場合は、ClassLoader の findLoadedClass メソッドを呼び出して、JVM がこのクラスをロードしたかどうかを確認します。ロードした場合は、直接戻ります。 clazz = findLoadedClass(名前); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("キャッシュからクラスを返す"); もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } 文字列リソース名 = binaryNameToPath(name, false); //この時点では、javaseClassLoader は拡張クラスローダーであり、拡張クラスローダーは javaseClassLoader に割り当てられます クラスローダー javaseLoader = getJavaseClassLoader(); ブール値の tryLoadingFromJavaseLoader; 試す { ..... // getResource で取得できれば // 拡張クラスローダーの getResource で取得できれば、拡張クラスローダーでロードできるということになります。次に、拡張クラスローダーがロードできるように手配します if (tryLoadingFromJavaseLoader) { 試す { //拡張クラスローダーを使用してロードします clazz = javaseLoader.loadClass(name); (clazz != null)の場合{ もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } } キャッチ (ClassNotFoundException e) { // 無視する } } // (0.5) SecurityManager 使用時にこのクラスにアクセスする権限 セキュリティマネージャが null の場合 int i = name.lastIndexOf('.'); もし (i >= 0) { 試す { セキュリティマネージャのパッケージアクセスをチェックします(名前のサブ文字列(0,i)); } キャッチ (SecurityException se) { 文字列エラー = "セキュリティ違反、" + を使用しようとしました 「制限クラス: 」+ 名前; log.info(エラー、se); 新しい ClassNotFoundException(error, se) をスローします。 } } } ブール型 delegateLoad = delegate || filter(name, true); // (1) 要求があれば親に委任する //trueの場合、親クラスローダーを使用してロードします。if (delegateLoad) { (log.isDebugEnabled())の場合 log.debug("親クラスローダー1に委任しています" + parent); 試す { clazz = Class.forName(名前、false、親); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("親からクラスをロードしています"); もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } } キャッチ (ClassNotFoundException e) { // 無視する } } // (2) ローカルリポジトリを検索 (log.isDebugEnabled())の場合 log.debug("ローカルリポジトリを検索しています"); 試す { // ローカルにロード clazz = findClass(name); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("ローカルリポジトリからクラスをロードしています"); もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } } キャッチ (ClassNotFoundException e) { // 無視する } // (3) 無条件に親に委譲する //まだロードされていません。親クラスローダーを使用して再度ロードしてみてください。if (!delegateLoad) { (log.isDebugEnabled())の場合 log.debug(" 最後に親クラスローダーに委任しています: " + parent); 試す { clazz = Class.forName(名前、false、親); (clazz != null)の場合{ (log.isDebugEnabled())の場合 log.debug("親からクラスをロードしています"); もし(解決する) クラスを解決します(clazz); 戻りクラッズ; } } キャッチ (ClassNotFoundException e) { // 無視する } } } 新しい ClassNotFoundException(名前) をスローします。 } 注: 英語のコメントの 37 行目には、システム クラス ローダーが取得されたと記載されていますが、デバッグしてみると、拡張クラス ローダーであることがわかります。実際には、ロードするクラスが拡張クラス ローダー パスの下にすでに存在する場合、システム クラス ローダーを直接呼び出すのは間違っているため、拡張クラス ローダーであるはずだと推測できます。次の図は、デバッグ後に取得されたクラス ローダーの検証を示しています。 要約するTomcat は親委任の原則に違反しており、実際にはアプリケーション クラス ローダー内の親委任を破壊していますが、他のクラス ローダーは依然として親委任に従っています。 Tomcat クラスローディングメカニズムに関するこの記事はこれで終わりです。Tomcat クラスローディングメカニズムの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: HTMLテーブルレイアウトの実践的な使い方の詳しい説明
Linuxシステムでは、dockerを新しくインストールし、次のようなコマンドを入力します。dock...
この記事では、例を使用して MySQL ビューの原理と使用方法を説明します。ご参考までに、詳細は以下...
1. はじめにDocker には、タスクを構成する複数の Docker コンテナをオーケストレーショ...
この記事では、CentOS 7.4 サーバーに Apache をインストールする方法と、インストール...
1. プロメテウスの紹介Prometheus は、もともと SoundCloud によって開発された...
この記事では、3階層ナビゲーションの表示と非表示を実現するためのVueの具体的なコードを例として紹介...
数日前、国産の XHTML エディタを紹介しました。今日は、有名な海外の Web デザイン ブログl...
1. はじめにMySQL ロックは、その範囲に応じて、グローバル ロック、テーブル ロック、行ロック...
1. フロート+オーバーフロー:非表示このメソッドは主にオーバーフローを通じて BFC をトリガーし...
CSS 位置position 属性は、要素の配置タイプを指定します。位置プロパティには 5 つの値が...
目次1. 使い方が簡単2. DISTINCTを使用して重複を削除する3. COUNT()の詳細な紹介...
ディレクトリを作成する cd /usr/local/docker/ jenkins-docker を...
目次フロントエンドルーティングとは何ですか?フロントエンドルーティングを実装するにはどうすればいいで...
1. 公式サイトにアクセスしてインストールパッケージをダウンロードしますダウンロードリンク: クリッ...
この記事の例では、書籍ショッピングカート機能を実現するためのVueの具体的なコードを参考までに共有し...