Location-Independent Access to Resources
- Overview
- Resources, names, andcontexts
- System Resources
- Non-System Resources
- Resource Names
- Using Methods of java.lang.Class
- Using Methods ofjava.lang.ClassLoader
- Security
- Examples
Overview
Zasoby to dane (obrazy, dźwięk, tekst i tak dalej), do których program musi mieć dostęp w sposób niezależny od lokalizacji kodu programu. Programy w Javie mogą korzystać z dwóch mechanizmów dostępu do zasobów: Aplety używają Applet.getCodeBase()
, aby uzyskać bazowy URL dla kodu apletu, a następnie rozszerzają bazowyURL o względną ścieżkę, aby załadować żądany zasób, na przykład za pomocą Applet.getAudioClip(url)
. Aplikacje używają „dobrze znanych lokalizacji”, takich jakSystem.getProperty("user.home")
lubSystem.getProperty("java.home")
, a następnie dodają”/lib/resource” i otwierają ten plik.
Metody w klasach Class
iClassLoader
zapewniają niezależny od lokalizacji sposób lokalizowania zasobów. Na przykład umożliwiają one lokalizowanie zasobów dla:
- Apletu załadowanego z Internetu przy użyciu wielu połączeń HTTP.
- Apletu załadowanego przy użyciu plików JAR.
- Beanu Java załadowanego lub zainstalowanego w CLASSPATH.
- Biblioteki zainstalowanej w CLASSPATH.
Metody te nie zapewniają specyficznej obsługi lokalizowania zlokalizowanych zasobów. Zlokalizowane zasoby są obsługiwane przez obiekty internacjonalizacji.
Zasoby, nazwy i konteksty
Zasób jest identyfikowany za pomocą łańcucha składającego się z sekwencji podłańcuchów, ograniczonych ukośnikami (/), po których następuje nazwa zasobu.Każdy podłańcuch musi być poprawnym identyfikatorem Java. Nazwa zasobu ma postać shortName
lubshortName.extension
. ZarównoshortName
jak i extension
muszą być identyfikatorami Javy.
Nazwa zasobu jest niezależna od implementacji Javy; w szczególności, separatorem ścieżki jest zawsze ukośnik(/). Jednak implementacja Javy kontroluje szczegóły tego, jak zawartość zasobu jest mapowana do pliku, bazy danych lub innego obiektu zawierającego rzeczywisty zasób.
Interpretacja nazwy zasobu jest względna dla instancji classloadera. Metody implementowane przez klasęClassLoader
dokonują tej interpretacji.
Zasoby systemowe
Zasób systemowy to zasób, który jest albo wbudowany w system, albo przechowywany przez implementację hosta w, na przykład, systemie plików lokalnych. Programy uzyskują dostęp do zasobów systemowych za pomocą metodClassLoader
getSystemResource
igetSystemResourceAsStream
.
Na przykład, w konkretnej implementacji, zlokalizowanie zasobu systemowego może wymagać przeszukania wpisów w CLASSPATH. MetodyClassLoader
przeszukują każdy katalog, plik ZIP lub JAR w CLASSPATH w poszukiwaniu pliku zasobu, a jeśli zostaną znalezione, zwracają albo InputStream
, albo nazwę zasobu. Jeśli nie zostanie znaleziony, metody zwracają null. Zasób może być znaleziony w innym wpisie w CLASSPATH niż lokalizacja, w której został załadowany plik klasy.
Zasoby niesystemowe
Wdrożenie getResource
na załadowanej klasie zależy od szczegółów klasy ClassLoader
. Na przykład, AppletClassLoader
:
- Najpierw próbuje zlokalizować zasób jako zasób systemowy; następnie, jeśli nie zostanie znaleziony,
- Przeszukuje zasoby w archiwach (plikach JAR) już załadowanych w tym CODEBASE; następnie, jeśli nie zostanie znaleziony,
- Używa CODEBASE i próbuje zlokalizować zasób (co może wymagać skontaktowania się ze zdalną witryną).
Wszystkie programy ładujące klasy będą szukały zasobu najpierw jako zasobu systemowego, w sposób analogiczny do szukania plików klas. Ta reguła wyszukiwania pozwala na nadpisanie lokalnie dowolnego zasobu. Clientsshould choose a resource name that will be unique (using thecompany or package name as a prefix, for instance).
Resource Names
A common convention for the name of a resource used by a classis to use the fully qualified name of the package of the class, butconvert all periods (.) to slashes (/), and add a resource name ofthe form name.extension
. Aby to obsłużyć i uprościć obsługę szczegółów klas systemowych (dla którychgetClassLoader
zwraca null), klasaClass
udostępnia dwie wygodne metody, które wywołują odpowiednie metody w ClassLoader
.
Nazwa zasobu podana metodzie Class
może mieć początkowy początek „/”, który identyfikuje ją jako nazwę „absolutną”.Nazwy zasobów, które nie zaczynają się od „/”, są „względne”.
Nazwy bezwzględne są pozbawiane początkowego „/” i są przekazywane, bez dalszych modyfikacji, do odpowiedniej metodyClassLoader
w celu zlokalizowania zasobu. Relativenames are modified according to the convention described previouslyand then are passed to a ClassLoader
method.
Using Methods of java.lang.Class
Klasa Class
implementuje kilka metod ładowania zasobów.
The method getResource()
returns a URL for theresource. Adres URL (i jego reprezentacja) jest specyficzny dla implementacji i maszyny JVM (to znaczy, adres URL uzyskany w jednej instancji czasu rzeczywistego może nie działać w innej). Jego protokół jest zwykle specyficzny dla ClassLoader
ładującego zasób. Jeśli zasób nie istnieje lub nie jest widoczny ze względu na względy bezpieczeństwa, metody zwracają wartość null.
Jeśli kod klienta chce odczytać zawartość zasobu jako InputStream
, może zastosować metodęopenStream()
na adresie URL. Jest to wystarczająco powszechne, aby uzasadnić dodanie getResourceAsStream()
doClass
i ClassLoader
.getResourceAsStream()
to samo, co wywołaniegetResource().openStream()
, z wyjątkiem tego, żegetResourceAsStream()
wyłapuje wyjątki IO, zwracając wartość anull InputStream
.
Kod klienta może również zażądać zawartości zasobu jako obiektu, stosując metodę java.net.URL.getContent()
na adresie URL. Jest to przydatne, gdy zasób zawiera na przykład dane obrazu. W przypadku obrazu wynikiem jest obiekt awt.image.ImageProducer
, a nie obiektImage
.
Metody getResource
igetResourceAsStream
znajdują zasób o podanej nazwie. Zwracają null, jeśli nie znajdą zasobu o podanej nazwie. Zasady wyszukiwania zasobów powiązanych z daną klasą są implementowane przez klasęClassLoader. Metody Class
delegują metodyClassLoader
, po zastosowaniu konwencji nazewnictwa: jeśli nazwa zasobu zaczyna się od „/”, to jest używana taka, jaka jest.W przeciwnym razie, nazwa pakietu jest poprzedzana, po konwersji wszystkich kropek (.) na ukośniki (/).
public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResourceAsStream(name); // A system class. } return cl.getResourceAsStream(name);}public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResource(name); // A system class. } return cl.getResource(name);}
Metoda resolveName
dodaje przedrostek nazwy pakietu, jeśli nazwa nie jest bezwzględna, i usuwa wszelkie wiodące „/”, jeśli nazwa jest bezwzględna. Możliwe jest, choć rzadko spotykane, posiadanie klas w różnych pakietach współdzielących ten sam zasób.
private String resolveName(String name) { if (name == null) { return name; } if (!name.startsWith("/")) { Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { name = baseName.substring(0, index).replace('.', '/') + "/" + name; } } else { name = name.substring(1); } return name;}
Usługiwanie się metodami java.lang.ClassLoader
Klasa ClassLoader
posiada dwa zestawy metod dostępu do zasobów. Jeden zestaw zwraca wartość InputStream
dla zasobu. Drugi zestaw zwraca adres URL. Metody zwracające InputStream
są łatwiejsze w użyciu i zaspokoją wiele potrzeb, natomiast metody zwracające adresy URL zapewniają dostęp do bardziej złożonych informacji, takich jak Image i AudioClip.
Klasa ClassLoader
zmienia zasoby podobnie do sposobu, w jaki zarządza klasami. A ClassLoader
kontroluje jak tomap nazwa zasobu do jego zawartości. ClassLoader
zapewnia również metody dostępu do zasobów systemowych, analogiczne do klas systemowych. Klasa Class
udostępnia pewne wygodne metody, które delegują funkcjonalność do metod ClassLoader
.
Wiele programów Java uzyska dostęp do tych metod pośrednio przez interfejsy API I18N (lokalizacja). Inne uzyskają do nich dostęp za pośrednictwem metod w Class
. Kilka z nich będzie bezpośrednio wywoływać metodyClassLoader
.
Metody w ClassLoader
używają podanego ciągu znaków jako nazwy zasobu bez stosowania żadnych przekształceń absolutnych/relatywnych (patrz metody w Class). Nazwa nie powinna mieć wiodącego „/”.
Zasoby systemowe to takie, które są obsługiwane bezpośrednio przez implementację hosta. Na przykład, mogą znajdować się w kataloguCLASSPATH.
Nazwa zasobu to rozdzielona znakiem „/” sekwencja identyfikatorów. Klasa Class
zapewnia wygodne metody dostępu do zasobów; metody te implementują konwencję, w której nazwa pakietu jest poprzedzona krótką nazwą zasobu.
Do zasobów można uzyskać dostęp w postaci InputStream
lubURL.
Metoda getSystemResourceAsStream
zwraca strumień wejściowy dla określonego zasobu systemowego lub wartość null, jeśli nie znajdzie zasobu. Nazwa zasobu może być dowolnym zasobem systemowym.
Metoda getSystemResource
znajduje zasób systemowy o podanej nazwie. Zwraca adres URL do zasobu lub null, jeśli nie znajdzie zasobu. Wywołanie metodyjava.net.URL.getContent()
z adresem URL spowoduje zwrócenie obiektu takiego jak ImageProducer
, AudioClip
lub InputStream
.
Metoda getResourceAsStream
zwraca obiektInputStream
dla określonego zasobu lub null, jeśli nie znajdzie zasobu.
Metoda getResource
znajduje zasób o określonej nazwie. Zwraca adres URL do zasobu lub null, jeśli nie znajdzie zasobu. Wywołaniejava.net.URL.getContent()
z adresem URL zwróci obiekt taki jak ImageProducer
, AudioClip
lub InputStream
.
Bezpieczeństwo
Ponieważ getResource()
zapewnia dostęp do informacji, musi mieć dobrze zdefiniowane i uzasadnione zasady bezpieczeństwa. Ifsecurity considerations do not allow a resource to be visible insome security context, the getResource()
method willfail (return null) as if the resource were not present at all, thisaddresses existence attacks.
Class loaders may not provide access to the contents of a .classfile for both security and performance reasons. To, czy możliwe jest uzyskanie adresu URL do pliku .class, zależy od specyfiki, jak pokazano poniżej.
Nie ma określonych kwestii bezpieczeństwa ani ograniczeń dotyczących zasobów, które są znajdowane przez niesystemowy program ładujący klasy.AppletClassLoader
zapewnia dostęp do informacji, które są ładowane z lokalizacji źródłowej, albo indywidualnie, albo w grupie za pośrednictwem pliku JAR; dlatego AppletClassLoader
powinien stosować te same reguły checkConnect()
przy postępowaniu z adresami URL za pośrednictwem getResource()
.
System ClassLoader
zapewnia dostęp do informacji w CLASSPATH. CLASSPATH może zawierać katalogi i pliki JAR. Ponieważ plik JAR jest tworzony celowo, ma on inne znaczenie niż katalog, w którym rzeczy mogą kończyć się w bardziej przypadkowy sposób. In particular, we are more strict on gettinginformation out of a directory than out from a JAR file.
If a resource is in a directory:
-
getResource()
invocations will useFile.exists()
to determine whether to make thecorresponding file visible to the user. Przypomnijmy, żeFile.exists()
używa metodycheckRead()
w menedżerze zabezpieczeń. - to samo dotyczy
getResourceAsStream()
.
If the resource is in a JAR file:
-
getResource()
invocations will succeed for allfiles, regardless of whether the invocation is done from within a system or a non-system class. -
getResourceAsStream()
inwokacje powiodą się dla zasobów innych niż .class, podobnie jak dlajava.net.URL.getContent()
na odpowiednich adresach URL.
Przykłady
Ta sekcja zawiera dwa przykłady kodu klienta. Pierwszy przykład wykorzystuje „bezwzględne nazwy zasobów” i tradycyjne mechanizmy do uzyskania obiektu Class
.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String absName = "/pkg/mumble.baf"; public static void test1() { Class c=null; try { c = Class.forName("pkg.Test"); } catch (Exception ex) { // This should not happen. } InputStream s = c.getResourceAsStream(absName); // do something with it. } public void test2() { InputStream s = this.getClass().getResourceAsStream(absName); // do something with it. }}
Ten przykład wykorzystuje „względne nazwy zasobów” i mechanizmy dostępne w kompilatorze za pośrednictwem flagi -experimental
, aby uzyskać obiekt Class
.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String relName = "mumble.baf"; public static void test1() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it.} public void test2() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. }
.