- Contents -
この記事では、
ClassLoader::gerResource
と、
ClassLoader::gerResources
の挙動について記載します。
同名リソースが複数ある場合、さきに見つかったリソースが取得される。
ClassLoader::gerResource
は、
クラスPATHの指定順に従ってファイル検索をしてくるため、
同名リソースが存在する場合は、
先にみつかったリソースが優先され、
クラスPATHの後ろの方にあたるリソースは取得されません。
def classLoader = getClass().getClassLoader()
def url = classLoader.getResource("sample.txt")
println new File(url.toURI()).getText()
//==> This resource is in main.
同名リソースがあると、
先にみつかったリソースが取得される。
リソースの重複とは
リソースの重複とはどういう状況か?
Javaはソースセットという単位でビルド単位を分割することができますので、
mainとtest、それぞれにresourcesディレクトリを
作成してリソースファイルを配置することができます。
- src/main/resources/sample.txt
- src/test/resources/sample.txt
のように同名リソースで重複が起こります。
ClassLoaderとは
リソース取得するには、クラスローダーを利用します。
classpathで指定したディレクトリ、jarファイルが保持している
リソースファイル、またはクラスを取得するためのクラスです。
System Class Loader
システムクラスローダー
またはアプリケーションクラスローダーと呼ばれるものがあります。
アプリケーションレベルのクラス専用のローダーになります。
Java起動時に、-classpath
もしくは -cp
といったコマンドラインのオプションでクラスパスを指定します。
または、CLASSPATH環境変数で指定する事も可能。
AppClassLoaderの実装
System Class Loader は、
実際には AppClassLoaderというクラス実装になっています。
※基本的にはそうですが、SystemClassLoaderは差し替えることも可能なため、フレームワークによっては異なる場合もあります。
ClassLoader cl = getClass().getClassLoader();
//==> jdk.internal.loader.ClassLoaders$AppClassLoader@2c13da15
AppClassLoaderクラスの継承関係
ClassLoader <- SecureClassLoader <- BuiltinClassLoader <- AppClassLoader
このAppClassLoaderクラスの実装は、
ClassPathに指定したURL(パッケージPATH)を順番にみてリソースを検索しています。
AppClassLoaderは、BuiltinClassLoaderを継承しています。
BuiltinClassLoader がメンバ変数でもっているURLClassPathクラスが
指定したクラスパスを順番に保持している。
// the URL class path, or null if there is no class path
private final URLClassPath ucp;
getResourcesを代わりにつかう
同名リソースがある場合は、
先にみつかったものを優先で取得しますが、
ClassLoaderにはgetResourceメソッドのほかに、
getResourcesというメソッドが用意されています。
ClassLoader::getResouces
は、
マッチするリソースをすべて取得する事が可能です。
返り値は、Enumration<URL>
で返してくるので、
Enumration::hasMoreElements
でチェックして、
Enumration::nextElement
で取得して利用します。
ClassLoader::getResouces
を利用した例
def classLoader = getClass().getClassLoader()
def enumerator = classLoader.getResources("sample.txt")
while (enumerator.hasMoreElements()) {
def url = enumerator.nextElement()
println new File(url.toURI()).getText()
}
//==> This resource is in test.
//==> This resource is in main.
ちなみに、ClassLoader::getResouceAsStream
というのもありますが、
こちらはgetResourceメソッドはURLで返すのに対し、
ファイルの内容をInputStreamクラスで取得する用のものです。
まとめ
ClassLoader::getResource
は、ClassPath指定順に検索し、
パッケージ内でさきに見つかったリソースを取得する。
重複パッケージも含めて、全リソースを取得したい場合は、
ClassLoader::getResources
を利用する。