Javaでリソースをロードする好ましい方法


107

Javaでリソースをロードする最良の方法を知りたいです。

  • this.getClass().getResource() (or getResourceAsStream())
  • Thread.currentThread().getContextClassLoader().getResource(name)
  • System.class.getResource(name)

回答:


140

必要に応じてソリューションを作成してください...

getResource/ getResourceAsStream()が呼び出されたクラスから/ が取得するものは2つあります...

  1. クラスローダー
  2. 開始場所

だからあなたがするなら

this.getClass().getResource("foo.txt");

「this」クラスと同じパッケージから、「this」クラスのクラスローダーを使用してfoo.txtをロードしようとします。「/」を前に置くと、リソースを完全に参照していることになります。

this.getClass().getResource("/x/y/z/foo.txt")

「this」のクラスローダーとxyzパッケージからリソースを読み込みます(パッケージ内のクラスと同じディレクトリにある必要があります)。

Thread.currentThread().getContextClassLoader().getResource(name)

コンテキストクラスローダーでロードされますが、パッケージに従って名前を解決しません(絶対参照する必要があります)

System.class.getResource(name)

システムクラスローダーを使用してリソースをロードします(java.langパッケージ(システムのパッケージ)に何も入れることができないため、絶対的に参照する必要もあります)。

ソースを見てください。また、getResourceAsStreamがgetResourceから返されたURLで「openStream」を呼び出し、それを返すことも示しています。


AFAIKパッケージの名前は重要ではありません。重要なのはクラスローダーのクラスパスです。
Bart van Heukelom、

@Bartソースコードを見ると、クラスでgetResourceを呼び出すときにクラス名が重要であることがわかります。この呼び出しが最初に行うことは、「resolveName」を呼び出すことです。これにより、必要に応じてパッケージプレフィックスが追加されます。resolveNameのJavadocは、「名前が絶対でない場合はパッケージ名の接頭辞を追加します。名前が絶対の場合は先頭の「/」を削除します」
Michael Wiles

10
ああ、なるほど。ここでの絶対とは、ファイルシステムの絶対ではなく、クラスパスに対する相対を意味します。
Bart van Heukelom、

1
リソースがクラスパス内にない場合はgetResourceAsStream()から返されるストリームがnullでないことを常に確認する必要があることを追加したいだけです。
stenix、2015年

また、コンテキストクラスローダーを使用すると、実行時にクラスローダーをを介して変更できることにも注意してくださいThread#setContextClassLoader。これは、プログラムの実行中にクラスパスを変更する必要がある場合に役立ちます。
最大

14

まあ、実際に派生クラスにいる場合に何をしたいかによっても異なります。

たとえば、SuperClassがA.jarとSubClassB.jarにありSuperClass、で宣言されているインスタンスメソッドでコードを実行しているが、はのthisインスタンスを参照しているとしますSubClass。使用this.getClass().getResource()するとSubClass、B.jarではに相対的に見えます。それは通常必要なことでないようです。

個人的に私はおそらくFoo.class.getResourceAsStream(name)最も頻繁に使用します-あなたが探しているリソースの名前をすでに知っていて、それがとの相対位置がわかっている場合は、それがFooIMOを実行する最も堅牢な方法です。

もちろん、それが望ましくない場合もあります。それぞれのケースをそのメリットで判断してください。「このリソースがこのクラスにバンドルされていることを知っている」というのは、私が遭遇した最も一般的なものです。


スキート:「スーパークラスのインスタンスメソッドでコードを実行していますが、これがサブクラスのインスタンスを参照しています」というステートメントに疑問がある場合、スーパークラスのインスタンスメソッド内でステートメントを実行すると、「これ」はスーパークラスを参照します。サブクラス。
デッドプログラマ

1
@Suresh:いいえ、ありません。それを試してみてください!2つのクラスを作成し、一方を他方から派生させてから、スーパークラスで出力しthis.getClass()ます。サブクラスのインスタンスを作成してメソッドを呼び出すと、スーパークラスではなくサブクラスの名前が出力されます。
Jon Skeet、

おかげでサブクラスのインスタンスメソッドはスーパークラスのメソッドを呼び出します。
デッドプログラマ

1
this.getResourceAsStreamを使用すると、this clasが存在するのと同じjarからのみリソースをロードでき、別のjarからはリソースをロードできないかどうか、私が疑問に思っています。私の考えでは、リソースをロードするのはクラスローダーであり、確かに1つのjarからのロードだけに制限されないのですか?
Michael Wiles、

10

以下のように3箇所検索します。コメントを歓迎します。

public URL getResource(String resource){

    URL url ;

    //Try with the Thread Context Loader. 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Let's now try with the classloader that loaded this class.
    classLoader = Loader.class.getClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Last ditch attempt. Get the resource from the classpath.
    return ClassLoader.getSystemResource(resource);
}

ありがとう、これは素晴らしいアイデアです。ちょうど私が必要としたもの。
devo、2012年

2
私はあなたのコードのコメントを見ていましたが、最後のコメントは面白そうですね。クラスパスからすべてのリソースが読み込まれているわけではありませんか?そして、ClassLoader.getSystemResource()は、上記が成功しなかった場合をカバーしますか?
nyxz 2015

正直なところ、3つの異なる場所からファイルをロードする理由がわかりません。ファイルの保存場所がわかりませんか?
bvdb 2017年

3

私は別の答えが本当に遅いのを知っていますが、私が最後に私を助けたものを共有したかっただけです。また、(クラスパスだけでなく)ファイルシステムの絶対パスからリソース/ファイルをロードします。

public class ResourceLoader {

    public static URL getResource(String resource) {
        final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
        classLoaders.add(Thread.currentThread().getContextClassLoader());
        classLoaders.add(ResourceLoader.class.getClassLoader());

        for (ClassLoader classLoader : classLoaders) {
            final URL url = getResourceWith(classLoader, resource);
            if (url != null) {
                return url;
            }
        }

        final URL systemResource = ClassLoader.getSystemResource(resource);
        if (systemResource != null) {
            return systemResource;
        } else {
            try {
                return new File(resource).toURI().toURL();
            } catch (MalformedURLException e) {
                return null;
            }
        }
    }

    private static URL getResourceWith(ClassLoader classLoader, String resource) {
        if (classLoader != null) {
            return classLoader.getResource(resource);
        }
        return null;
    }

}

0

上記の方法や機能をたくさん試しましたが、私のプロジェクトではうまくいきませんでした。とにかく私は解決策を見つけました、そしてそれはここにあります:

try {
    InputStream path = this.getClass().getClassLoader().getResourceAsStream("img/left-hand.png");
    img = ImageIO.read(path);
} catch (IOException e) {
    e.printStackTrace();
}

this.getClass().getResourceAsStream()この場合は、より適切に使用する必要があります。getResourceAsStreamメソッドのソースを見ると、それがあなたと同じことをしていることに気づくでしょう。ClassLoader行われていることがわかりますクラスに見つから)。また、あなたは可能性が発生する可能性があることを示しているnullgetClassLoader...あなたのコードで
ドクDavluz

@PromCompotは、私が言ったthis.getClass().getResourceAsStream()ように私のために働いていないので、私はそれがうまくいくように使用します。私のような問題に直面できる人もいると思います。
Vladislav
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.