Java'da kaynakları yüklemenin tercih edilen yolu


107

Java'da bir kaynak yüklemenin en iyi yolunu bilmek istiyorum:

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

Yanıtlar:


140

Çözümü istediğiniz şeye göre çalışın ...

Çağrıldığı sınıftan alacağı getResource/ getResourceAsStream()alacağı iki şey var ...

  1. Sınıf yükleyici
  2. Başlangıç ​​konumu

Yani eğer yaparsan

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

foo.txt dosyasını "this" sınıfıyla aynı paketten ve "this" sınıfının sınıf yükleyicisiyle yüklemeye çalışır. Önüne bir "/" koyarsanız, o zaman kesinlikle kaynağa atıfta bulunuyorsunuz.

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

kaynağı "this" sınıf yükleyicisinden ve xyz paketinden yükler (bu paketteki sınıflarla aynı dizinde olması gerekir).

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

bağlam sınıfı yükleyici ile yüklenecek, ancak adı herhangi bir pakete göre çözümlemeyecektir (kesinlikle referans verilmelidir)

System.class.getResource(name)

Kaynağı sistem sınıfı yükleyiciyle yükleyecektir (java.lang paketine (Sistem paketi) hiçbir şey koyamayacağınız için kesinlikle başvurulması gerekir.

Sadece kaynağa bir bakın. Ayrıca getResourceAsStream'in getResource'tan döndürülen URL'de yalnızca "openStream" i çağırdığını ve bunu döndürdüğünü belirtir.


AFAIK paket adlarının bir önemi yoktur, bunu yapan sınıf yükleyicinin sınıf yoludur.
Bart van Heukelom

@Bart, kaynak koduna bakarsanız, bir sınıfta getResource'u çağırdığınızda sınıf adının önemli olduğunu fark edeceksiniz. Bu çağrının yaptığı ilk şey, uygunsa paket önekini ekleyen "resolName" çağrısıdır. SolutionName için Javadoc, "Ad mutlak değilse bir paket adı öneki ekle" Ad mutlaksa baştaki "/" öğesini kaldır "
Michael Wiles

10
Ah, anlıyorum. Burada mutlak, dosya sistemi mutlakından ziyade sınıf yoluna göreli anlamına gelir.
Bart van Heukelom

1
Kaynak sınıf yolu içinde değilse, getResourceAsStream () 'den döndürülen akışın boş olmadığını her zaman kontrol etmeniz gerektiğini eklemek istiyorum.
stenix

Ayrıca, bağlam sınıfı yükleyiciyi kullanmanın, sınıf yükleyicinin aracılığıyla çalışma zamanında değiştirilmesine izin verdiğini belirtmek gerekir Thread#setContextClassLoader. Bu, program çalışırken sınıf yolunu değiştirmeniz gerekirse kullanışlıdır.
Maksimum

14

Eh, kısmen sen eğer ne istediğinizi bağlıdır aslında türetilmiş bir sınıfta.

Örneğin, SuperClassA.jar'da olduğunu ve B.jar'da olduğunu ve SubClassiçinde bildirilen SuperClassancak burada thisbir örneğini ifade eden bir örnek yönteminde kod çalıştırdığınızı varsayalım SubClass. Eğer kullanırsanız this.getClass().getResource()kavramaya göre bakacağız SubClassB.jar içinde. Sanırım genellikle gerekli olan bu değil.

Şahsen muhtemelen Foo.class.getResourceAsStream(name)en sık kullanırdım - peşinde olduğunuz kaynağın adını zaten biliyorsanız ve bunun neresine göre olduğundan eminseniz, FooIMO'yu yapmanın en sağlam yolu budur.

Elbette , istediğiniz şeyin bu olmadığı zamanlar da vardır: her vakayı kendi yararına göre değerlendirin. Karşılaştığım en yaygın kaynak sadece "Bu kaynağın bu sınıfla birlikte olduğunu biliyorum".


skeet: "SuperClass'ın bir örnek yönteminde kod çalıştırıyorsunuz, ancak bunun bir SubClass örneğini ifade ettiği durumlarda" ifadesindeki bir şüphe, süper sınıfın örnek yöntemi içinde ifadeleri çalıştırıyorsak, "bu", üst sınıfa değil alt sınıf.
Ölü Programcı

1
@Suresh: Hayır olmayacak. Dene! Birinin diğerinden türetilmesini ve ardından üst sınıfın çıktısını alarak iki sınıf oluşturun this.getClass(). Alt sınıfın bir örneğini oluşturun ve yöntemi çağırın ... üst sınıfın değil, alt sınıfın adını yazdıracaktır.
Jon Skeet

teşekkürler alt sınıf örnek yöntemi süper sınıf yöntemini çağırır.
Ölü Programcı

1
Merak ettiğim şey, this.getResourceAsStream'i kullanmanın, yalnızca bu sınıf başka bir kavanozdan değil de aynı kavanozdan bir kaynak yükleyip yükleyemeyeceğidir. Benim hesabıma göre, kaynağı yükleyen sınıf yükleyicidir ve kesinlikle tek bir kavanozdan yüklenmekle sınırlandırılmayacak?
Michael Wiles

10

Aşağıda gösterildiği gibi üç yeri araştırıyorum. Yorumlar hoş geldiniz.

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

Teşekkürler, bu harika bir fikir. Tam ihtiyacım olan şey.
devo

2
Kodunuzdaki yorumlara bakıyordum ve sonuncusu kulağa ilginç geliyor. Tüm kaynaklar sınıf yolundan yüklenmedi mi? Ve ClassLoader.getSystemResource () yukarıdakilerin başarılı olamadığını hangi durumlarda kapsar?
nyxz

Dürüst olmak gerekirse, neden 3 farklı yerden dosya yüklemek istediğinizi anlamıyorum. Dosyalarınızın nerede saklandığını bilmiyor musunuz?
bvdb

3

Başka bir cevap için çok geç olduğunu biliyorum ama sonunda bana yardımcı olan şeyi paylaşmak istedim. Ayrıca, dosya sisteminin mutlak yolundan da kaynakları / dosyaları yükleyecektir (yalnızca sınıf yollarının değil).

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

Yukarıda önerilen birçok yolu ve işlevi denedim, ancak projemde işe yaramadı. Her neyse, bir çözüm buldum ve işte burada:

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

this.getClass().getResourceAsStream()Bu durumda kullanmalısın . getResourceAsStreamYöntemin kaynağına bir göz atarsanız, sizden aynı şeyi yaptığını ancak daha akıllıca ( ClassLoadersınıfta hiç bulunamıyorsa geri dönüş) yaptığını fark edeceksiniz . Ayrıca kodunuzda bir potansiyel nullile karşılaşabileceğinizi de gösterir getClassLoader
Doc Davluz

@ PromCompot, dediğim gibi benim this.getClass().getResourceAsStream()için çalışmıyor, bu yüzden işe yarıyor kullanıyorum. Sanırım benimki gibi sorunlarla yüzleşebilecek bazı insanlar var.
Vladislav
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.