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:

  1. Trovare la Class o ClassLoader che troverà la risorsa.
  2. Trovare la risorsa
  3. Ottenere il flusso di byte per la risorsa.
  4. Lettura ed elaborazione del flusso di byte.
  5. 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'oggetto Class per il tipo String .

  • Object.getClass() ti darà l'oggetto Class per il tipo od qualsiasi oggetto; ad esempio "hello".getClass() è un altro modo per ottenere la Class del tipo String .

  • Il Class.forName(String) (se necessario) carica dinamicamente una classe e restituisce il suo oggetto Class ; ad esempio Class.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 e Class 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.
  • Se non è possibile trovare la risorsa richiesta (o le risorse), i methods return getResource e getResourceAsStream methods return null , and the methods return an empty getResources methods return an empty un'enumerazione vuota`.

  • Gli URL restituiti saranno risolvibili utilizzando URL.toStream() . Potrebbero essere file: URL o altri URL convenzionali, ma se la risorsa risiede in un file JAR, saranno jar: URL che identificano il file JAR e una risorsa specifica al suo interno.

  • Se il codice utilizza un metodo getResourceAsStream (o URL.toStream() ) per ottenere un InputStream , è responsabile della chiusura dell'oggetto flusso. La mancata chiusura del flusso potrebbe causare una perdita di risorse.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow