Szukaj…


Wprowadzenie

Java umożliwia pobieranie zasobów opartych na plikach przechowywanych w pliku JAR wraz ze skompilowanymi klasami. Ten temat koncentruje się na ładowaniu tych zasobów i udostępnianiu ich kodowi.

Uwagi

Zasób to dane podobne do pliku o nazwie podobnej do ścieżki, które znajdują się w ścieżce klasy. Najczęstszym zastosowaniem zasobów jest łączenie obrazów aplikacji, dźwięków i danych tylko do odczytu (takich jak konfiguracja domyślna).

Dostęp do zasobów można uzyskać za pomocą metod ClassLoader.getResource i ClassLoader.getResourceAsStream . Najczęstszym przypadkiem użycia jest umieszczenie zasobów w tym samym pakiecie co klasa, która je czyta; metody Class.getResource i Class.getResourceAsStream obsługują ten typowy przypadek użycia.

Jedyną różnicą między metodą getResource a metodą getResourceAsStream jest to, że ta pierwsza zwraca adres URL, a druga otwiera ten adres URL i zwraca InputStream.

Metody ClassLoadera akceptują ścieżkową nazwę zasobu jako argument i przeszukują każdą lokalizację w ścieżce klas ClassLoadera w poszukiwaniu pozycji pasującej do tej nazwy.

  • Jeśli lokalizacja ścieżki klasy jest plikiem .jar, wpis jar o określonej nazwie jest uważany za zgodny.
  • Jeśli lokalizacją ścieżki klasy jest katalog, plik względny w tym katalogu o określonej nazwie jest uważany za zgodny.

Nazwa zasobu jest podobna do części ścieżki względnego adresu URL. Na wszystkich platformach używa ukośników do przodu ( / ) jako separatorów katalogów. Nie może zaczynać się od ukośnika.

Odpowiednie metody klasy są podobne, z wyjątkiem:

  • Nazwa zasobu może zaczynać się od ukośnika, w którym to przypadku początkowy ukośnik jest usuwany, a reszta nazwy jest przekazywana do odpowiedniej metody ClassLoader.
  • Jeśli nazwa zasobu nie zaczyna się od ukośnika, jest traktowana jako relatywna do klasy, której metoda getResource lub getResourceAsStream jest wywoływana. Rzeczywista nazwa zasobu staje się pakiet / nazwa , gdzie pakiet jest nazwą pakietu, do którego należy klasa, z każdym okresem zastępowanym ukośnikiem, a nazwa jest oryginalnym argumentem podanym metodzie.

Na przykład:

package com.example;

public class ExampleApplication {
    public void readImage()
    throws IOException {

        URL imageURL = ExampleApplication.class.getResource("icon.png");

        // The above statement is identical to:
        // ClassLoader loader = ExampleApplication.class.getClassLoader();
        // URL imageURL = loader.getResource("com/example/icon.png");

        Image image = ImageIO.read(imageURL);
    }
}

Zasoby powinny być umieszczane w nazwanych pakietach, a nie w katalogu głównym pliku .jar, z tego samego powodu, w którym klasy są umieszczane w pakietach: Aby uniknąć kolizji między wieloma dostawcami. Na przykład, jeśli wiele plików .jar znajduje się w ścieżce klasy, a więcej niż jeden z nich zawiera wpis config.properties w katalogu głównym, wywołania metod getResource lub getResourceAsStream zwrócą config.properties z dowolnego z nich .jar wymienionego jako pierwszy w ścieżka klasy. Nie jest to przewidywalne zachowanie w środowiskach, w których kolejność ścieżek klas nie jest pod bezpośrednią kontrolą aplikacji, takich jak Java EE.

Wszystkie metody getResource i getResourceAsStream zwracają null jeśli określony zasób nie istnieje. Ponieważ zasoby muszą być dodane do aplikacji w czasie kompilacji, ich lokalizacje powinny być znane podczas pisania kodu; niepowodzenie w znalezieniu zasobu w czasie wykonywania jest zwykle wynikiem błędu programisty.

Zasoby są tylko do odczytu. Nie ma sposobu, aby napisać do zasobu. Początkujący programiści często popełniają błąd, zakładając, że ponieważ zasób jest oddzielnym plikiem fizycznym podczas tworzenia w środowisku IDE (takim jak Eclipse), w ogólnym przypadku można bezpiecznie traktować go jak oddzielny plik fizyczny. Nie jest to jednak poprawne; aplikacje są prawie zawsze dystrybuowane jako archiwa, takie jak pliki .jar lub .war, w takich przypadkach zasób nie będzie osobnym plikiem i nie będzie można go zapisywać. (Metoda getFile klasy URL nie jest obejściem tego problemu; pomimo jej nazwy zwraca jedynie fragment ścieżki adresu URL, co nie jest gwarancją, że jest prawidłową nazwą pliku).

Nie ma bezpiecznego sposobu na wyświetlenie listy zasobów w czasie wykonywania. Ponownie, ponieważ programiści są odpowiedzialni za dodawanie plików zasobów do aplikacji w czasie kompilacji, programiści powinni już znać swoje ścieżki. Chociaż istnieją obejścia, nie są one niezawodne i ostatecznie zakończą się niepowodzeniem.

Ładowanie obrazu z zasobu

Aby załadować dołączony obraz:

package com.example;

public class ExampleApplication {
    private Image getIcon() throws IOException {
        URL imageURL = ExampleApplication.class.getResource("icon.png");
        return ImageIO.read(imageURL);
    }
}

Ładowanie domyślnej konfiguracji

Aby odczytać domyślne właściwości konfiguracji:

package com.example;

public class ExampleApplication {
    private Properties getDefaults() throws IOException {
        Properties defaults = new Properties();

        try (InputStream defaultsStream =
            ExampleApplication.class.getResourceAsStream("config.properties")) {

            defaults.load(defaultsStream);
        }

        return defaults;
    }
}

Ładowanie zasobu o tej samej nazwie z wielu plików JAR

Zasób o tej samej ścieżce i nazwie może znajdować się w więcej niż jednym pliku JAR w ścieżce klasy. Typowe przypadki to zasoby zgodne z konwencją lub będące częścią specyfikacji opakowania. Przykładami takich zasobów są

  • META-INF / MANIFEST.MF
  • META-INF / beans.xml (specyfikacja CDI)
  • Właściwości ServiceLoader zawierające dostawców implementacji

Aby uzyskać dostęp do wszystkich tych zasobów w różnych słoikach, należy użyć ClassLoadera, który ma do tego odpowiednią metodę. Zwrócone Enumeration można wygodnie przekonwertować na List za pomocą funkcji Kolekcje.

Enumeration<URL> resEnum = MyClass.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
ArrayList<URL> resources = Collections.list(resEnum);

Znajdowanie i czytanie zasobów za pomocą modułu ładującego klasy

Ładowanie zasobów w Javie obejmuje następujące kroki:

  1. Znalezienie Class lub ClassLoader , który znajdzie zasób.
  2. Znalezienie zasobu.
  3. Uzyskiwanie strumienia bajtów dla zasobu.
  4. Odczytywanie i przetwarzanie strumienia bajtów.
  5. Zamykanie strumienia bajtów.

Ostatnie trzy kroki zwykle wykonuje się przez przekazanie adresu URL do metody bibliotecznej lub konstruktora w celu załadowania zasobu. W takim przypadku zazwyczaj używasz metody getResource . Możliwe jest również odczytanie danych zasobów w kodzie aplikacji. W takim przypadku zwykle używasz getResourceAsStream .

Bezwzględne i względne ścieżki zasobów

Zasoby, które można załadować ze ścieżki klas, są oznaczone ścieżką . Składnia ścieżki jest podobna do ścieżki pliku UNIX / Linux. Składa się z prostych nazw oddzielonych ukośnikiem ( / ). Ścieżka względna zaczyna się od nazwy, a ścieżka bezwzględna zaczyna się od separatora.

Jak opisano w przykładach Classpath, ścieżka klasy JVM definiuje przestrzeń nazw poprzez nałożenie przestrzeni nazw katalogów i plików JAR lub ZIP w ścieżce klasy. Po rozstrzygnięciu ścieżki bezwzględnej moduły ładujące klasy interpretują wartość początkową / jako oznaczającą katalog główny przestrzeni nazw. Natomiast ścieżka względna może zostać rozstrzygnięta względem dowolnego „folderu” w przestrzeni nazw. Używany folder będzie zależał od obiektu użytego do rozwiązania ścieżki.

Uzyskiwanie klasy lub modułu ładującego klasy

Zasób można zlokalizować przy użyciu obiektu Class lub obiektu ClassLoader . Obiekt Class może rozpoznawać ścieżki względne, dlatego zazwyczaj używasz jednej z nich, jeśli masz zasób względny (klasy). Istnieje wiele sposobów uzyskania obiektu Class . Na przykład:

  • Literał klasy da ci obiekt Class dla dowolnej klasy, którą możesz nazwać w kodzie źródłowym Java; np. String.class daje ci obiekt Class dla typu String .

  • Object.getClass() da ci obiekt Class dla typu dowolnego obiektu; np. "hello".getClass() to inny sposób na uzyskanie Class typu String .

  • Metoda Class.forName(String) razie potrzeby dynamicznie załaduje klasę i zwróci jej obiekt Class ; np. Class.forName("java.lang.String") .

Obiekt ClassLoader jest zwykle uzyskiwany przez wywołanie getClassLoader() na obiekcie Class . Możliwe jest również przejęcie domyślnego modułu ClassLoader.getSystemClassLoader() JVM za pomocą statycznej metody ClassLoader.getSystemClassLoader() .

get metody

Po utworzeniu instancji Class lub ClassLoader możesz znaleźć zasób, korzystając z jednej z następujących metod:

Metody Opis
ClassLoader.getResource(path)
ClassLoader.getResources(path)
Zwraca adres URL reprezentujący lokalizację zasobu o podanej ścieżce.
ClassLoader.getResources(path)
Class.getResources(path)
Zwraca Enumeration<URL> podające adresy URL, których można użyć do zlokalizowania zasobu foo.bar ; patrz poniżej.
ClassLoader.getResourceAsStream(path)
Class.getResourceStream(path)
Zwraca InputStream z którego można odczytać zawartość zasobu foo.bar jako sekwencję bajtów.

Uwagi:

  • Główna różnica między wersjami metod ClassLoader i Class polega na sposobie interpretacji ścieżek względnych.

    • Metody Class rozwiązują ścieżkę względną w „folderze”, która odpowiada pakietowi klas.
    • Metody ClassLoader traktują ścieżki względne tak, jakby były absolutne; tzn. rozwiąż je w „folderze głównym” przestrzeni nazw ścieżki klasy.
  • Jeśli nie można znaleźć żądanego zasobu (lub zasobów), metody getResource i getResourceAsStream methods return wartość null , and the methods return an empty getResources methods return an empty Wyliczenie`.

  • Zwrócone adresy URL można rozwiązać za pomocą URL.toStream() . Mogą to być file: adresy URL lub inne konwencjonalne adresy URL, ale jeśli zasób znajduje się w pliku JAR, będą to jar: adresy URL identyfikujące plik JAR i określony zasób w jego obrębie.

  • Jeśli kod używa metody getResourceAsStream (lub URL.toStream() ) do uzyskania InputStream , odpowiada za zamknięcie obiektu strumienia. Brak zamknięcia strumienia może doprowadzić do wycieku zasobów.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow