Java開発を行うとき、クラスローダーメカニズムに精通している必要がある基本的な知識。この記事では、Javaクラスローダーメカニズムを簡単に要約します。異なるJVMの実装は異なるため、この記事で説明するコンテンツはHotspot JVMに限定されています。
この記事では、JDKが提供するClassLoaderの4つの側面、親委任モデル、Javaの親代表団を破るクラスローダーをカスタマイズする方法から始まります。
JDKデフォルトのクラスローダー
JDKは、デフォルトで次のクラスローダーを提供します
bootstrpローダー
bootstrpローダーは、C ++言語で記述されています。 Java仮想マシンが開始された後に初期化されます。それは主に、%java_home%/jre/lib、-xbootclasspathパラメーター、および%java_home%/jre/classesのクラスで指定されたパスをロードする責任があります。
extclassloader
bootstrpローダーはextclassloaderをロードし、extclassloaderの親ローダーをbootstrp loader.extclassloaderに設定します。 extclassloaderは、主に%Java_home%/jre/lib/extをロードし、java.ext.dirsシステム変数によって指定されたパスのこのパスおよびクラスライブラリのすべてのクラスディレクトリをロードします。
AppClassLoader
bootstrpローダーがextclassloaderをロードすると、AppClassloaderがロードされ、AppClassLoaderの親ローダーがExtClassLoaderとして指定されます。 AppClassLoaderもJavaで書かれています。その実装クラスは、sun.misc.launcher $ appclassloaderです。さらに、Classloaderにはgetsystemclassloaderメソッドがあることがわかっています。このメソッドは、AppClassLoader.AppClassLoaderを返します。主にClassPathで指定された場所にクラスまたはJARドキュメントをロードする責任があります。また、Javaプログラムのデフォルトのクラスローダーでもあります。
親委任モデル
Javaでのクラスローダーの読み込みは、親の委任メカニズムを採用しています。親代表団のメカニズムを使用してクラスをロードする場合、次の手順が採用されています。
現在、ClassLoaderは最初に、このクラスがすでにロードされているクラスからロードされているかどうかを確認します。ロードされている場合、元のクラスを直接返します。
各クラスのローダーには、独自のロードキャッシュがあります。クラスがロードされると、キャッシュに入れられ、次回ロードされると直接返すことができます。
クラスローダーのキャッシュが見つからない場合、親クラスローダーはロードするために委任されます。親クラスローダーは同じ戦略を採用しています。まず、独自のキャッシュをチェックしてから、親クラスの親クラスを委任してロードします。
すべての親クラスローダーがロードされていない場合、それらは現在のクラスローダーによってロードされ、独自のキャッシュに入れて、次回ロードリクエストがあるときに直接返すことができます。
これについて言えば、なぜJavaはそのような委任メカニズムを採用しているのだろうと思うかもしれません。この問題を理解するには、クラスローダーに関する別の概念「名前空間」を紹介します。つまり、特定のクラスを決定するには、クラスの完全な資格のある名前が必要であり、このクラスクラスローダーをロードして共同で決定する必要があります。つまり、2つのクラスの完全な資格のある名前が同じであっても、このクラスが異なるクラスをロードするため、JVMの別のクラスです。名前空間を理解した後、デリゲートモデルを見てみましょう。デリゲートモデルを採用した後、さまざまなクラスローダーのインタラクティブな機能が増加します。たとえば、上記のように、Hashmap、LinkedListなど、JDK Binshengが提供するクラスライブラリ。これらのクラスがBootStrpクラスローダーによってロードされた後、プログラムにあるクラスローダーの数に関係なく、これらのクラスは実際に共有できます。
クラスローダーをカスタマイズする方法
上記のデフォルトで提供されているクラスローダーに加えて、Javaでは、アプリケーションがクラスローダーをカスタマイズすることもできます。クラスローダーをカスタマイズする場合は、java.lang.classloaderを継承してクラスローダーを実装する必要があります。次に、クラスローダーをカスタマイズするときに注意する必要があるいくつかの重要な方法を見てみましょう。
1.ロードクラスメソッド
LoadClassメソッドは宣言します
public class <?> loadclass(string name)classNotFoundExceptionをスローします
上記は、LoadClassメソッドのプロトタイプ宣言です。上記の親委任メカニズムの実装は、この方法で実際に実装されています。この方法のコードを見て、それが親の委任をどのように実施するかを見てみましょう。
LoadClassメソッド実装
パブリッククラス<?
上記から、LoadClassメソッドがLoadCclass(Name、False)メソッドを呼び出すことがわかります。そのため、別のLoadClassメソッドの実装を見てみましょう。
クラスLoadClass(String Name、Boolean Resolve)
保護された同期クラス<?ローダーが指定され、親ローダーが委任されてロードされます。 } else {c = findbootstrapclass0(name); //親クラスローダーがない場合は、ブートストラップローダーを委任してロードして}} catch(classNotFoundException e){//順番でfindclassを呼び出してください//クラスを見つける。 c = findclass(name); //親クラスの読み込みがロードされていない場合、独自のFindClassを介してロードされます。 }} if(resolve){resolveclass(c);} return c;}上記のコードでは、コメントを追加して、LoadClassの親委任メカニズムがどのように機能するかを明確に確認しました。ここで注意する必要があることの1つは、Public Class <?さらに、上記のFindClassメソッドがあることに気付きました。次に、この方法が悪いかどうかについて話しましょう。
2.ファインドクラス
java.lang.classloaderのソースコードを確認すると、FindClassの実装は次のとおりです。
Protected class <?
この方法のデフォルトの実装は、例外を直接スローすることであることがわかりますが、実際、この方法はオーバーライドするためにアプリケーションに任されています。特定の実装は、実装ロジックに依存します。ディスクから読み取るか、ネットワークからクラスファイルのバイトストリームを取得できます。クラスバイナリを取得した後、それを削除して、さらなるロードのためにdefinclassに引き渡すことができます。 DefanyClassを後で説明しましょう。 OK、上記の分析を通じて、次の結論を描くことができます。
独自のクラスローダーを書くときは、親の委任メカニズムに従いたい場合は、FindClassをオーバーライドするだけです。
3。defineclass
まず、defineclassのソースコードを見てみましょう。
defineclass
保護された最終クラス<?
上記のコードから、この方法は最終として定義されていることがわかります。つまり、この方法をオーバーライドできないことを意味します。実際、これはJVMから残された唯一のエントリでもあります。このユニークなエントリを通じて、JVMは、クラスファイルがJava仮想マシン仕様で指定されたクラスの定義に準拠する必要があることを保証します。この方法は、最終的にネイティブメソッドを呼び出して、実際のクラスの負荷を実装します。
わかりました、上記の説明を通して、次の質問について考えてみましょう。
Java.lang.Stringクラスを自分で書いた場合、JDK自体を呼び出すクラスを交換できますか?
答えはノーです。達成することはできません。なぜ?親の委任メカニズムがこの問題を解決するという多くのオンライン説明が見られますが、実際にはあまり正確ではありません。親の委任メカニズムを破ることができるため、クラスローダーを書いて、書いたjava.lang.Stringクラスをロードできますが、正常にロードされないことがわかります。具体的には、Javaから始まるクラスの場合、JVMの実装により、Bootstrpがロードする必要があります。
「親委任メカニズム」に従わないシナリオ
上記は、親の委任メカニズムは主に、異なるクラスローダー間でロードされたクラスの相互作用の問題を実現することであると述べました。全員が共有されるクラスは親ローダーに引き渡されてロードされますが、実際には、親クラスローダーがロードするクラスが子ドローダーによってロードされたクラスを使用する必要があるJavaには実際に状況があります。この状況の発生について話しましょう。
Javaには、JDBC、JNDIなどのSPIライブラリを使用するSPI(ServiceProviderInterface)標準があります。JDBCには第三者が提供するドライバーが必要であり、ドライバーのJARパッケージがアプリケーション自体のクラスパスに配置されていることを知っています。 JDBC自体のAPIは、JDKが提供するJDKの一部であり、Bootstrpによってロードされています。では、サードパーティのメーカーが提供する実装クラスをロードするにはどうすればよいですか? Javaは、スレッドコンテキストクラスの読み込みの概念を紹介します。スレッドクラスローダーは、デフォルトで親スレッドから継承されます。指定されていない場合、デフォルトはSystem Class Loader(AppClassLoader)です。このようにして、サードパーティのドライバーがロードされると、スレッドのコンテキストクラスローダーを介してロードできます。
さらに、より柔軟なクラスローダーOSGIといくつかのJavaAppServersを実装するために、親の委任メカニズムも破損します。
要約します
上記は、Javaクラスローダーメカニズムの使用と使用に関するコード分析に関するこの記事のすべての内容です。私はそれが誰にでも役立つことを願っています。興味のある友人は、このサイトの他の関連トピックを引き続き参照できます。欠点がある場合は、それを指摘するためにメッセージを残してください。このサイトへのご支援をありがとうございました!