Java Language
Risorse (sul classpath)
Ricerca…
introduzione
Java consente il recupero di risorse basate su file memorizzate all'interno di un JAR insieme a classi compilate. Questo argomento si concentra sul caricamento di tali risorse e sulla loro disponibilità per il tuo codice.
Osservazioni
Una risorsa è data da file con un nome simile a un percorso, che risiede nel classpath. L'uso più comune delle risorse è il raggruppamento di immagini di applicazioni, suoni e dati di sola lettura (come la configurazione di default).
È possibile accedere alle risorse con i metodi ClassLoader.getResource e ClassLoader.getResourceAsStream . Il caso d'uso più comune è disporre di risorse inserite nello stesso pacchetto della classe che le legge; i metodi Class.getResource e Class.getResourceAsStream servono questo caso di utilizzo comune.
L'unica differenza tra un metodo getResource e il metodo getResourceAsStream è che il primo restituisce un URL, mentre quest'ultimo apre quell'URL e restituisce un InputStream.
I metodi di ClassLoader accettano un nome di risorsa simile a un percorso come argomento e ricercano ogni posizione nel classpath del ClassLoader per una voce corrispondente a tale nome.
- Se una posizione del percorso di classe è un file .jar, una voce di jar con il nome specificato è considerata una corrispondenza.
- Se una posizione del percorso di classe è una directory, un file relativo in tale directory con il nome specificato è considerato una corrispondenza.
Il nome della risorsa è simile alla porzione di percorso di un URL relativo. Su tutte le piattaforme, utilizza le barre ( /
) come separatori di directory. Non deve iniziare con una barra.
I metodi corrispondenti di Class sono simili, ad eccezione di:
- Il nome della risorsa può iniziare con una barra, nel qual caso la barra iniziale viene rimossa e il resto del nome viene passato al metodo corrispondente di ClassLoader.
- Se il nome della risorsa non inizia con una barra, viene considerato come relativo alla classe il cui metodo getResource o getResourceAsStream viene chiamato. Il nome effettivo della risorsa diventa package / name , dove package è il nome del pacchetto a cui appartiene la classe, con ogni periodo sostituito da una barra, e name è l'argomento originale dato al metodo.
Per esempio:
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);
}
}
Le risorse dovrebbero essere collocate in pacchetti con nome, piuttosto che nella radice di un file .jar, per lo stesso motivo le classi sono collocate in pacchetti: per evitare collisioni tra più fornitori. Ad esempio, se più file .jar si trovano nel classpath e più di uno di essi contiene una voce config.properties
nella sua radice, le chiamate ai metodi getResource o getResourceAsStream restituiranno i file config.properties da qualsiasi .jar è elencato per primo in il classpath. Questo comportamento non è prevedibile in ambienti in cui l'ordine del classpath non è sotto il controllo diretto dell'applicazione, come Java EE.
Tutti i metodi getResource e getResourceAsStream restituiscono null
se la risorsa specificata non esiste. Poiché le risorse devono essere aggiunte all'applicazione al momento della compilazione, le loro posizioni dovrebbero essere note al momento della scrittura del codice; un errore nel trovare una risorsa in fase di esecuzione è in genere il risultato di un errore del programmatore.
Le risorse sono di sola lettura. Non c'è modo di scrivere su una risorsa. Gli sviluppatori principianti spesso commettono l'errore di assumere che dal momento che la risorsa è un file fisico separato durante lo sviluppo in un IDE (come Eclipse), sarà sicuro trattarlo come un file fisico separato nel caso generale. Tuttavia, questo non è corretto; le applicazioni sono quasi sempre distribuite come archivi come file .jar o .war e, in questi casi, una risorsa non sarà un file separato e non sarà scrivibile. (Il metodo getFile della classe URL non è una soluzione per questo, nonostante il suo nome, restituisce semplicemente la porzione di percorso di un URL, che non è in alcun modo garantito che sia un nome file valido).
Non esiste un modo sicuro per elencare le risorse in fase di runtime. Ancora una volta, poiché gli sviluppatori sono responsabili dell'aggiunta di file di risorse all'applicazione al momento della compilazione, gli sviluppatori dovrebbero già conoscere i loro percorsi. Mentre ci sono soluzioni alternative, non sono affidabili e alla fine falliranno.
Caricamento di un'immagine da una risorsa
Per caricare un'immagine in bundle:
package com.example;
public class ExampleApplication {
private Image getIcon() throws IOException {
URL imageURL = ExampleApplication.class.getResource("icon.png");
return ImageIO.read(imageURL);
}
}
Caricamento della configurazione predefinita
Per leggere le proprietà di configurazione predefinite:
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;
}
}
Caricamento della risorsa con lo stesso nome da più JAR
Risorsa con lo stesso percorso e nome possono esistere in più di un file JAR sul classpath. I casi comuni sono risorse che seguono una convenzione o che fanno parte di una specifica di imballaggio. Esempi per tali risorse sono
- META-INF / MANIFEST.MF
- META-INF / beans.xml (Specifiche CDI)
- Proprietà ServiceLoader contenenti provider di implementazione
Per accedere a tutte queste risorse in diversi jar, è necessario utilizzare ClassLoader, che ha un metodo per questo. L' Enumeration
restituita può essere convenientemente convertita in una List
utilizzando una funzione Collections.
Enumeration<URL> resEnum = MyClass.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
ArrayList<URL> resources = Collections.list(resEnum);
Trovare e leggere le risorse usando un classloader
Il caricamento delle risorse in Java comprende i seguenti passaggi:
- Trovare la
Class
oClassLoader
che troverà la risorsa. - Trovare la risorsa
- Ottenere il flusso di byte per la risorsa.
- Lettura ed elaborazione del flusso di byte.
- Chiusura del flusso di byte.
Gli ultimi tre passaggi vengono in genere eseguiti passando l'URL a un metodo o costruttore di libreria per caricare la risorsa. In questo caso, in genere utilizzi un metodo getResource
. È anche possibile leggere i dati della risorsa nel codice dell'applicazione. In questo caso, in genere si utilizza getResourceAsStream
.
Percorsi di risorse assoluti e relativi
Le risorse che possono essere caricate dal classpath sono contraddistinte da un percorso . La sintassi del percorso è simile a un percorso file UNIX / Linux. Consiste di nomi semplici separati da caratteri di barra ( /
). Un percorso relativo inizia con un nome e un percorso assoluto inizia con un separatore.
Come descrivono gli esempi di Classpath, il classpath di una JVM definisce uno spazio dei nomi sovrapponendo gli spazi dei nomi delle directory e dei file JAR o ZIP nel classpath. Quando viene risolto un percorso assoluto, i classloader interpretano l'iniziale /
come significato della radice dello spazio dei nomi. Al contrario, un percorso relativo può essere risolto rispetto a qualsiasi "cartella" nello spazio dei nomi. La cartella utilizzata dipenderà dall'oggetto che si utilizza per risolvere il percorso.
Ottenere una classe o un classloader
Una risorsa può essere localizzata usando un oggetto Class
o un oggetto ClassLoader
. Un oggetto di Class
può risolvere percorsi relativi, quindi in genere si utilizzerà uno di questi se si dispone di una risorsa relativa (di classe). Esistono diversi modi per ottenere un oggetto Class
. Per esempio:
Un letterale di classe ti darà l'oggetto
Class
per qualsiasi classe che puoi nominare nel codice sorgente Java; ad es.String.class
fornisce l'oggettoClass
per il tipoString
.Object.getClass()
ti darà l'oggettoClass
per il tipo od qualsiasi oggetto; ad esempio"hello".getClass()
è un altro modo per ottenere laClass
del tipoString
.Il
Class.forName(String)
(se necessario) carica dinamicamente una classe e restituisce il suo oggettoClass
; ad esempioClass.forName("java.lang.String")
.
Un oggetto ClassLoader
viene in genere ottenuto chiamando getClassLoader()
su un oggetto Class
. È anche possibile ottenere il classloader predefinito della JVM utilizzando il metodo ClassLoader.getSystemClassLoader()
statico.
I metodi get
Una volta che hai un'istanza Class
o ClassLoader
, puoi trovare una risorsa, usando uno dei seguenti metodi:
metodi | Descrizione |
---|---|
ClassLoader.getResource(path) ClassLoader.getResources(path) | Restituisce un URL che rappresenta la posizione della risorsa con il percorso specificato. |
ClassLoader.getResources(path) Class.getResources(path) | Restituisce Enumeration<URL> fornisce gli URL che possono essere utilizzati per individuare la risorsa foo.bar ; vedi sotto. |
ClassLoader.getResourceAsStream(path) Class.getResourceStream(path) | Restituisce un InputStream dal quale è possibile leggere il contenuto della risorsa foo.bar come una sequenza di byte. |
Gli appunti:
La differenza principale tra le versioni
ClassLoader
eClass
dei metodi è nel modo in cui i percorsi relativi vengono interpretati.- I metodi
Class
risolvono un percorso relativo nella "cartella" che corrisponde al pacchetto classes. - I metodi
ClassLoader
trattano i percorsi relativi come se fossero assoluti; cioè risolvili nella "cartella radice" dello spazio dei nomi del classpath.
- I metodi
Se non è possibile trovare la risorsa richiesta (o le risorse), i
methods return
getResource
e getResourceAsStreammethods return
null, and the
methods return an empty
getResourcesmethods return an empty
un'enumerazione vuota`.Gli URL restituiti saranno risolvibili utilizzando
URL.toStream()
. Potrebbero esserefile:
URL o altri URL convenzionali, ma se la risorsa risiede in un file JAR, sarannojar:
URL che identificano il file JAR e una risorsa specifica al suo interno.Se il codice utilizza un metodo
getResourceAsStream
(oURL.toStream()
) per ottenere unInputStream
, è responsabile della chiusura dell'oggetto flusso. La mancata chiusura del flusso potrebbe causare una perdita di risorse.