HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

getResourceメソッドに指定するリソース名

指定した名前を持つリソース(ファイルなど)のURLを取得するためのgetResourceメソッドjava.lang.Classクラスとjava.lang.ClassLoaderクラスで定義されているが、パスの指定方法が時々わからなくなるので、ちょっと整理してみる。
まず、java.lang.ClassクラスのgetResourceメソッドJavaDocを見ると、以下のように書いてある。

The rules for searching resources associated with a given class are implemented by the defining class loader of the class. This method delegates to this object's class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResource(java.lang.String).

つまり、基本的には、このメソッドjava.lang.ClassLoaderクラスのgetResourceメソッドへ処理を委譲する。
java.lang.ClassクラスのJavaDocをさらに見ていくと、委譲する際のリソース名に対する処理について書いてある。

Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:

  • If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
  • Otherwise, the absolute name is of the following form:

    modified_package_name/name
    

    Where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').


つまり、以下の2つが同じリソースを取得するということ (この辺がいつも混乱する)。

Hoge.class.getResource("/hoge.txt");
Hoge.class.getClassLoader().getResource("hoge.txt");

以下の2つは異なる (厳密には、Hogeクラスのパッケージ名に依存する)。

Hoge.class.getResource("/hoge.txt");
Hoge.class.getResource("hoge.txt");

java.lang.ClassクラスのgetResourceメソッドの処理を単純化すると以下のような感じ。

if (name.startsWith("/")) {
    name = name.substring(1);
} else {
    String clsName = this.getName();  // "this" is an instance of java.lang.Class class
    int index = clsName.lastIndexOf(".");
    if (index != -1) {
        name = clsName.substring(0, index).replaceAll("[.]", "/") + "/" + name;
    }
}
ClassLoader cl = getClassLoader();
if (cl != null) {
    return cl.getResource(name);
} else {
    return ClassLoader.getSystemResource(name);
}

java.lang.ClassLoaderクラスのgetResourceメソッドの方は少しだけ厄介で、シンプルに

public class Hoge {
    public static void main(String[] args) {
        String name1 = "foo.txt";
        String name2 = "/" + name1;

        System.out.println("1: " + Hoge.class.getClassLoader().getResource(name1));
        System.out.println("2: " + Hoge.class.getClassLoader().getResource(name2));
    }
}

とした場合には、2つめはnullが返されるが、java.lang.ClassLoaderクラスのgetResourceメソッドやfindResourceメソッドをオーバーライドしたクラスローダーを作ってしまえばこの辺の動作を変えることができてしまう‥

詳しくは、java.lang.Classやjava.lang.ClassLoaderのソースをどうぞ(謎)