Java Language
クラスローダー
サーチ…
備考
クラスローダは、アプリケーションが使用するクラスの場所とロードを仲介することを主目的とするクラスです。クラスローダーは、 リソースを見つけてロードすることもできます。
標準のクラスローダークラスは、クラスとリソースをファイルシステム内のディレクトリとJARファイルとZIPファイルからロードできます。また、リモートサーバーからJARファイルとZIPファイルをダウンロードしてキャッシュすることもできます。
クラスローダーは通常はチェーンされているため、JVMはアプリケーション提供のソースよりも標準クラス・ライブラリーのクラスをロードしようとします。カスタムクラスローダーを使用すると、プログラマーはこれを変更できます。また、バイトコードファイルの解読やバイトコードの変更なども行うことができます。
クラスローダのインスタンス化と使用
この基本的な例では、アプリケーションがクラスローダをインスタンス化してクラスを動的にロードする方法を示します。
URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");
この例で作成されたクラスローダーは、デフォルトのクラスローダーを親として持ち、 "extra.jar"を見る前に親クラスローダーのクラスを見つけようとします。要求されたクラスが既にロードされている場合、 findClassコールは以前にロードされたクラスへの参照を返します。
findClass呼び出しは、さまざまな方法で失敗する可能性があります。最も一般的なのは次のとおりです。
- 指定されたクラスが見つからない場合、
ClassNotFoundExceptionをスローして呼び出しClassNotFoundException。 - 指定されたクラスが見つからない他のクラスに依存する場合、その呼び出しは
NoClassDefFoundErrorをスローします。
カスタムクラスローダーの実装
すべてのカスタムローダーは、 java.lang.ClassLoaderクラスを直接的または間接的に拡張する必要がありjava.lang.ClassLoader 。主な拡張ポイントは、次のメソッドです。
-
findClass(String)- クラスローダーがクラスローディングの標準委譲モデルに従っている場合は、このメソッドをオーバーロードします。 -
loadClass(String, boolean)- 代替の委譲モデルを実装するためにこのメソッドをオーバーロードします。 -
findResourceとfindResources- これらのメソッドをオーバーロードして、リソースの読み込みをカスタマイズします。
実際にバイト配列からクラスをロードする役割を担うdefineClassメソッドは、オーバーロードを防ぐためにfinalものです。 defineClassを呼び出す前にカスタムビヘイビアを実行する必要がありdefineClass 。
バイト配列から特定のクラスをロードする簡単な例を次に示します。
public class ByteArrayClassLoader extends ClassLoader {
private String classname;
private byte[] classfile;
public ByteArrayClassLoader(String classname, byte[] classfile) {
this.classname = classname;
this.classfile = classfile.clone();
}
@Override
protected Class findClass(String classname) throws ClassNotFoundException {
if (classname.equals(this.classname)) {
return defineClass(classname, classfile, 0, classfile.length);
} else {
throw new ClassNotFoundException(classname);
}
}
}
findClassメソッドをオーバーライドしただけなので、このカスタムクラスローダーはloadClassが呼び出されたときに次のように動作します。
- クラスローダの
loadClassメソッドはfindLoadedClassを呼び出して、この名前のクラスがこのクラスローダによってすでにロードされているかどうかを確認します。成功した場合、結果のClassオブジェクトはリクエスタに返されます。 - 次に、
loadClassメソッドは、loadClass呼び出しを呼び出すことによって親クラスローダーに委譲します。親が要求を処理することができる場合、親オブジェクトはClassオブジェクトを返し、要求者に返されます。 - 親クラスローダーがクラスをロードできない場合、
findClassはオーバーライドfindClassメソッドを呼び出し、ロードされるクラスの名前を渡します。 - 要求された名前が
this.classnameと一致する場合は、defineClassを呼び出してthis.classfileバイト配列から実際のクラスをロードします。結果のClassオブジェクトが返されます。 - 名前が一致しない場合、
ClassNotFoundExceptionをスローしClassNotFoundException。
外部.classファイルのロード
クラスをロードするには、最初にクラスを定義する必要があります。クラスはClassLoaderによって定義されます。 1つの問題がありますが、OracleはClassLoaderのコードをこの機能を利用できるように書いていませんでした。クラスを定義するには、 ClassLoaderプライベートメソッドであるdefineClass()という名前のメソッドにアクセスする必要があります。
これにアクセスするには、新しいクラスByteClassLoader作成し、それをClassLoader拡張します。クラスをClassLoader拡張したので、 ClassLoaderのプライベートメソッドにアクセスできます。 defineClass()使用できるようにするために、privateのdefineClass()メソッドのミラーのように動作する新しいメソッドを作成します。私たちは、クラス名、必要になりますプライベートメソッドを呼び出すには、 name 、クラスバイト、 classBytesになります最初のバイトのオフセットを、 0のでclassBytesのデータは、から始まりclassBytes[0]そして最後のバイトのオフセットたのだろう、 classBytes.lenghtはデータのサイズを表すため、最後のオフセットになります。
public class ByteClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] classBytes) {
return defineClass(name, classBytes, 0, classBytes.length);
}
}
さて、私たちはpublic defineClass()メソッドを持っていdefineClass() 。これは、クラスの名前とクラスのバイトを引数として渡すことで呼び出すことができます。
stackoverflowパッケージにMyClassという名前のクラスがあるとしましょう...
メソッドを呼び出すにはクラスバイトが必要です。クラスのPath表すPathオブジェクトをPaths.get()メソッドを使用してPaths.get() 、バイナリクラスのパスを引数として渡します。これで、クラスバイトをFiles.readAllBytes(path)で取得できます。そこで、 ByteClassLoaderインスタンスを作成し、作成したメソッドdefineClass()を使用します。私たちはすでにクラスバイトを持っていますが、メソッドを呼び出すためにはクラス名が必要です。このクラス名はクラス名(この場合はstackoverflow.MyClass )のパッケージ名(ドット)で与えられます。
Path path = Paths.get("MyClass.class");
ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);
注意 : defineClass()メソッドはClass<?>オブジェクトを返します。あなたが望むなら、それを保存することができます。
クラスをロードするには、 loadClass()を呼び出してクラス名を渡します。このメソッドはClassNotFoundExceptionをスローして、try cathブロックを使用する必要があります
try{
loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
e.printStackTrace();
}