Java Language
Zasoby (na ścieżce klasy)
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:
- Znalezienie
Class
lubClassLoader
, który znajdzie zasób. - Znalezienie zasobu.
- Uzyskiwanie strumienia bajtów dla zasobu.
- Odczytywanie i przetwarzanie strumienia bajtów.
- 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 obiektClass
dla typuString
.Object.getClass()
da ci obiektClass
dla typu dowolnego obiektu; np."hello".getClass()
to inny sposób na uzyskanieClass
typuString
.Metoda
Class.forName(String)
razie potrzeby dynamicznie załaduje klasę i zwróci jej obiektClass
; 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
iClass
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.
- Metody
Jeśli nie można znaleźć żądanego zasobu (lub zasobów), metody
getResource
i getResourceAsStreammethods return
wartość null, and the
methods return an empty
getResourcesmethods 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ą tojar:
adresy URL identyfikujące plik JAR i określony zasób w jego obrębie.Jeśli kod używa metody
getResourceAsStream
(lubURL.toStream()
) do uzyskaniaInputStream
, odpowiada za zamknięcie obiektu strumienia. Brak zamknięcia strumienia może doprowadzić do wycieku zasobów.