Qual è la differenza tra Class.getResource () e ClassLoader.getResource ()?


194

Mi chiedo quale sia la differenza tra Class.getResource()e ClassLoader.getResource()?

modifica: in particolare voglio sapere se è coinvolta la memorizzazione nella cache a livello di file / directory. Come in "gli elenchi di directory sono memorizzati nella cache nella versione Class?"

AFAIK quanto segue dovrebbe essenzialmente fare lo stesso, ma non lo sono:

getClass().getResource() 
getClass().getClassLoader().getResource()

L'ho scoperto mentre cercavo un po 'di codice per la generazione di report che crea un nuovo file WEB-INF/classes/da un file esistente in quella directory. Quando ho usato il metodo di Class, sono riuscito a trovare i file presenti al momento della distribuzione utilizzando getClass().getResource(), ma quando ho cercato di recuperare il file appena creato, ho ricevuto un oggetto null. La navigazione nella directory mostra chiaramente che il nuovo file è lì. I nomi dei file sono stati anteposti con una barra come in "/myFile.txt".

La ClassLoaderversione di getResource()d'altra parte ha trovato il file generato. Da questa esperienza sembra che ci sia una specie di memorizzazione nella cache dell'elenco delle directory in corso. Ho ragione, e se sì, dove è documentato?

Dai documenti API in poiClass.getResource()

Trova una risorsa con un determinato nome. Le regole per la ricerca di risorse associate a una determinata classe sono implementate dal caricatore di classi di definizione della classe. Questo metodo delega al programma di caricamento classi di questo oggetto. Se questo oggetto è stato caricato dal programma di caricamento classi bootstrap, il metodo delega a ClassLoader.getSystemResource (java.lang.String).

Per me, questo dice "Class.getResource sta davvero chiamando getResource ()" del proprio classloader. Sarebbe lo stesso che fare getClass().getClassLoader().getResource(). Ma ovviamente non lo è. Qualcuno potrebbe fornirmi un po 'di illuminazione in questa faccenda?

Risposte:


6

Per rispondere alla domanda se è in corso la memorizzazione nella cache.

Ho analizzato ulteriormente questo punto eseguendo un'applicazione Java autonoma che ha caricato continuamente un file dal disco utilizzando il metodo getResourceAsStream ClassLoader. Sono stato in grado di modificare il file e le modifiche sono state immediatamente riflesse, ovvero il file è stato ricaricato dal disco senza memorizzazione nella cache.

Tuttavia: sto lavorando a un progetto con diversi moduli maven e progetti Web che hanno dipendenze reciproche. Sto usando IntelliJ come mio IDE per compilare ed eseguire i progetti web.

Ho notato che quanto sopra sembrava non essere più vero, il motivo è che il file che stavo caricando è ora cotto in un barattolo e distribuito nel progetto web dipendente. L'ho notato solo dopo aver provato a cambiare il file nella mia cartella di destinazione, senza risultati. Ciò ha fatto sembrare che ci fosse la cache in corso.


Anch'io ho usato Maven e IntelliJ, quindi questa è la risposta in un ambiente che si avvicina di più al mio e ha una spiegazione ragionevole per la domanda n. 2.
Oligofren,

251

Class.getResourcepuò assumere un nome di risorsa "relativo", che viene trattato rispetto al pacchetto della classe. In alternativa è possibile specificare un nome di risorsa "assoluto" utilizzando una barra iniziale. I percorsi delle risorse del classloader sono sempre considerati assoluti.

Quindi i seguenti sono sostanzialmente equivalenti:

foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");

E così sono questi (ma sono diversi da quelli sopra):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");

Bella risposta con esempi chiari. Sebbene il post in realtà fosse destinato a ottenere risposte a due domande, ora vedo che la seconda è nascosta. Piuttosto incerto su come / se dovrei aggiornare il post per riflettere questo, ma quello che vorrei sapere secondo è questo (prossimo commento):
oligofren

2
Esiste un tipo di memorizzazione nella cache nella versione Class.getResource ()? Ciò che mi ha fatto credere che questa sia la generazione di alcuni report jasper: Usiamo getClass (). GetResource ("/ aDocument.jrxml") per recuperare il file xml jasper. Un file jasper binario viene quindi prodotto nella stessa directory. getClass (). getResource ("/ aDocument.jasper") non è in grado di trovarlo, sebbene possa trovare chiaramente documenti allo stesso livello (il file di input). È qui che ClassLoader.getResource () si è rivelato utile, poiché sembra che non usi la memorizzazione nella cache dell'elenco delle directory. Ma non riesco a trovare documentazione su questo.
Oligofren,

2
@oligofren: Hmm ... Non mi aspetto che Class.getResource () esegua la memorizzazione nella cache lì ...
Jon Skeet

1
@JonSkeet perché this.getClass().getClassLoader().getResource("/");restituire null? Non dovrebbe essere lo stesso dithis.getClass().getClassLoader().getResource(".");
Asif Mushtaq,

@UnKnown: penso che dovresti probabilmente fare una nuova domanda a riguardo.
Jon Skeet,

22

La prima chiamata cerca in relazione al .classfile mentre la seconda cerca in relazione alla radice del percorso di classe.

Per eseguire il debug di problemi del genere, stampo l'URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );

3
Penso che "root classloader" sarebbe più preciso di "root classpath" - solo per essere pignoli.
Jon Skeet,

4
Entrambi possono cercare "percorsi assoluti" se il nome del file è preceduto da "/"
oligofren

2
Interessante ... Sto colpendo un caso in cui getClass (). GetResource ("/ someAbsPath") restituisce un URL nel tipo /path/to/mylib.jar!/someAbsPath e getClass (). GetClassLoafer (). GetResource (" / someAbsPath ") return null ... Quindi" root of classloader "non sembra essere una nozione molto ben definita ...
Pierre Henry,


8
@PierreHenry: getClassLoader().getResource("/...")ritorna sempre null- il classloader non rimuove il lead /dal percorso, quindi la ricerca fallisce sempre. getClass().getResource()Gestisce solo un inizio /come percorso assoluto rispetto al percorso di classe.
Aaron Digulla,

17

Ho dovuto cercarlo nelle specifiche:

GetResource () della classe - la documentazione indica la differenza:

Questo metodo delega la chiamata al proprio caricatore di classi, dopo aver apportato queste modifiche al nome della risorsa: se il nome della risorsa inizia con "/", rimane invariato; in caso contrario, il nome del pacchetto viene anteposto al nome della risorsa dopo la conversione "." per "/". Se questo oggetto è stato caricato dal caricatore bootstrap, la chiamata viene delegata a ClassLoader.getSystemResource.


2
Hai informazioni sul fatto che memorizzi anche nella cache l'elenco delle directory? Questa era la differenza principale tra i due metodi quando si cerca per la prima volta un file di input e quindi si crea un file usando quello nella stessa directory. La versione Class non l'ha trovata, la versione ClassLoader l'ha trovata (entrambe usando "/file.txt").
Oligofren,

11

Tutte queste risposte qui intorno, così come le risposte a questa domanda , suggeriscono che il caricamento di URL assoluti, come "/foo/bar.properties", ha trattato lo stesso di class.getResourceAsStream(String)e class.getClassLoader().getResourceAsStream(String). Questo NON è il caso, almeno non nella mia configurazione / versione di Tomcat (attualmente 7.0.40).

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

Mi dispiace, non ho assolutamente una spiegazione soddisfacente, ma immagino che Tomcat faccia trucchi sporchi e la sua magia nera con i caricatori di classe e faccia la differenza. Ho sempre usato class.getResourceAsStream(String)in passato e non ho avuto problemi.

PS: l'ho anche pubblicato qui


Questo comportamento sembra essere un bug in Tomcat che è stato corretto nella versione 8. Ho aggiunto un paragrafo su questo nella mia risposta a questa domanda
LordOfThePigs

2

Class.getResourcesrecupererebbe la risorsa dal classloader che carica l'oggetto. Mentre ClassLoader.getResourcerecupererebbe la risorsa usando il classloader specificato.


0

Ho provato a leggere da input1.txt che era dentro uno dei miei pacchetti insieme alla classe che stava cercando di leggerlo.

Le seguenti opere:

String fileName = FileTransferClient.class.getResource("input1.txt").getPath();

System.out.println(fileName);

BufferedReader bufferedTextIn = new BufferedReader(new FileReader(fileName));

La parte più importante era chiamare getPath()se si desidera il nome percorso corretto in formato String. NON UTILIZZAREtoString() perché aggiungerà del testo di formattazione aggiuntivo che TOTALMENTE TOTALE il nomefile (puoi provarlo e vedere la stampa).

Ho trascorso 2 ore a eseguire il debug di questo ... :(



Le risorse non sono file. Potrebbero non essere decompressi dal file JAR o WAR e, in caso contrario, FileReadero FileInputStreamnon possono essere utilizzati per accedervi. La risposta non è corretta
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.