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();
}