JVMには、C ++で記述されたクラスローダーとJavaで記述されたクラスローダーの2種類があります。C ++で記述されているBootstrapClass Loader(Bootstrap Class Loader)を除いて、その他はJavaで記述されています。Javaで記述されたクラスローダーは、クラスjava.lang.ClassLoaderから継承します。
さまざまなクラスローダー間に論理的な親子関係があります
BootClassLoader
スタートアップクラスローダーはC ++で記述されており、スタートアップクラスローダーのコアは次のロジックです。
java.c、入り口LoadMainClass
LoadMainClassは主にcheckAndLoadMainを呼び出し、GetLauncherHelperClassはGetLauncherHelperClassクラスを見つけるために使用され、checkAndLoadMainはGetLauncherHelperClassにあります
checkAndLoadMainは、主にmain関数が配置されているクラスをロードするためのものです。この時点で、拡張クラスローダーとアプリケーションクラスローダーの起動も完了します。
int JNICALL
JavaMain(void * _args)
{
...
mainClass = LoadMainClass(env, mode, what);
...
}
static jclass
LoadMainClass(JNIEnv *env, int mode, char *name)
{
jclass cls = GetLauncherHelperClass(env);
...
NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
"checkAndLoadMain",
"(ZILjava/lang/String;)Ljava/lang/Class;"));
...
}
jclass
GetLauncherHelperClass(JNIEnv *env)
{
if (helperClass == NULL) {
NULL_CHECK0(helperClass = FindBootStrapClass(env,
"sun/launcher/LauncherHelper"));
}
return helperClass;
}
概要:このロジックのセットは、クラスローダーを起動してsun.launcher.LauncherHelperクラスをロードし、このクラスのcheckAndLoadMainメソッドを実行して、最後にメイン関数が配置されているクラスのロードを終了します。
親の代表団
親の委任:クラスローダーが特定のクラスをロードする要求を受信した場合、クラスローダーはクラスをロードしませんが、親クラスローダーに要求を委任します。クラスローダーの各レベルにはこれが含まれているため、すべてのクラスのロードリクエストは最終的に最上位のスタートアップクラスローダーに送信されます。親クラスローダーが検索範囲内で必要なクラスを見つけることができず、結果が子クラスローダーにフィードバックされる場合にのみ、子クラスローダーはそれをロードしようとします。それ自体で。
親の代表団を破る
場合によっては、親クラスローダーが子クラスローダーに委任してクラスファイルをロードする必要があるためです。ロード範囲の制限により、親クラスローダーは必要なファイルをロードできません
例としてjdbcを取り上げます。jdbcはjdkで定義され、その実装は各データベースのサービスプロバイダーによって提供されるため、問題は、DriverManager(jdkによっても提供される)がjdbcインターフェイスを実装する各実装クラスをロードする必要があることです。次にManagementですが、DriverManagerはスタートアップクラスローダーによってロードされ、その実装はサービスプロバイダーによって提供され、アプリケーションクラスローダーによってロードされます。今回は、クラスローダーを起動して、サブクラスに実装クラスのロードを委託する必要があります。したがって、親のデリゲートを破壊します。
親の委任を破ることは、実際には委任せず、下向きに委任することを意味します
親の委任の2つの方法を破る
SPI
SPIはサービス検出メカニズムです。ClassPathパスの下のMETA-INF / servicesフォルダーでファイルを検索し、ファイルで定義されたクラスを自動的にロードします。
SPIメカニズムの下方委任は親の委任を中断します
カスタムクラスローダー
カスタムクラスローダーの例:
classloadの
擬似コードを書き直します:
// 打破双亲委派
if (name.startsWith("com.XXX")) {
// com.XXX包下的所有类不委派
c = findClass(name);
} else {
// 其他包委派
c = this.getParent().loadClass(name);
}