Was ist der Unterschied zwischen Class.getResource () und ClassLoader.getResource ()?

194

Ich frage mich, was der Unterschied zwischen Class.getResource()und ist ClassLoader.getResource().

Bearbeiten: Ich möchte insbesondere wissen, ob es sich um Caching auf Datei- / Verzeichnisebene handelt. Wie in "Werden Verzeichnislisten in der Klassenversion zwischengespeichert?"

AFAIK die folgenden sollten im Wesentlichen das gleiche tun, aber sie sind nicht:

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

Ich habe dies entdeckt, als ich mit Code zur Berichterstellung herumgespielt habe, der eine neue Datei WEB-INF/classes/aus einer vorhandenen Datei in diesem Verzeichnis erstellt. Bei Verwendung der Methode aus Class konnte ich Dateien finden, die bei der Bereitstellung vorhanden getClass().getResource()waren. Beim Versuch, die neu erstellte Datei abzurufen, wurde jedoch ein Nullobjekt empfangen. Das Durchsuchen des Verzeichnisses zeigt deutlich, dass die neue Datei vorhanden ist. Den Dateinamen wurde ein Schrägstrich wie in "/myFile.txt" vorangestellt.

Die ClassLoaderVersion von hat getResource()andererseits die generierte Datei gefunden. Aus dieser Erfahrung geht hervor, dass die Verzeichnisliste zwischengespeichert wird. Habe ich recht und wenn ja, wo ist das dokumentiert?

Aus der API - Dokumentation aufClass.getResource()

Findet eine Ressource mit einem bestimmten Namen. Die Regeln für die Suche nach Ressourcen, die einer bestimmten Klasse zugeordnet sind, werden vom definierenden Klassenlader der Klasse implementiert. Diese Methode wird an den Klassenladeprogramm dieses Objekts delegiert. Wenn dieses Objekt vom Bootstrap-Klassenladeprogramm geladen wurde, delegiert die Methode an ClassLoader.getSystemResource (java.lang.String).

Für mich lautet dies "Class.getResource ruft wirklich getResource () seines eigenen Klassenladeprogramms auf". Welches wäre das gleiche wie zu tun getClass().getClassLoader().getResource(). Aber es ist offensichtlich nicht. Könnte mir bitte jemand etwas Licht in diese Angelegenheit bringen?

Oligofren
quelle

Antworten:

6

Um die Frage zu beantworten, ob Caching stattfindet.

Ich habe diesen Punkt weiter untersucht, indem ich eine eigenständige Java-Anwendung ausgeführt habe, die kontinuierlich eine Datei mit der Methode getResourceAsStream ClassLoader von der Festplatte geladen hat. Ich konnte die Datei bearbeiten und die Änderungen wurden sofort wiedergegeben, dh die Datei wurde ohne Zwischenspeichern von der Festplatte neu geladen.

Allerdings: Ich arbeite an einem Projekt mit mehreren Maven-Modulen und Webprojekten, die voneinander abhängig sind. Ich verwende IntelliJ als meine IDE, um die Webprojekte zu kompilieren und auszuführen.

Ich bemerkte, dass das oben Gesagte nicht mehr zuzutreffen schien. Der Grund dafür war, dass die Datei, die ich geladen habe, jetzt in ein Glas gebacken und für das entsprechende Webprojekt bereitgestellt wird. Ich habe dies erst bemerkt, nachdem ich versucht hatte, die Datei in meinem Zielordner zu ändern, ohne Erfolg. Dies ließ es so aussehen, als würde Caching stattfinden.

mchlstckl
quelle
Ich habe auch Maven und IntelliJ verwendet, daher ist dies die Antwort in einer Umgebung, die meiner am ehesten entspricht und eine vernünftige Erklärung für Frage 2 enthält.
Oligofren
249

Class.getResourcekann einen "relativen" Ressourcennamen annehmen, der relativ zum Paket der Klasse behandelt wird. Alternativ können Sie einen "absoluten" Ressourcennamen mithilfe eines führenden Schrägstrichs angeben. Classloader-Ressourcenpfade gelten immer als absolut.

Das Folgende ist also grundsätzlich gleichwertig:

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

Und so sind diese (aber sie unterscheiden sich von den oben genannten):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Jon Skeet
quelle
Schöne Antwort mit klaren Beispielen. Obwohl der Beitrag eigentlich dazu gedacht war, Antworten auf zwei Fragen zu erhalten, sehe ich jetzt, dass die zweite Frage irgendwie versteckt ist. Ganz unsicher, wie / ob ich den Beitrag aktualisieren soll, um dies widerzuspiegeln, aber was ich als zweites wissen möchte, ist
Folgendes
2
Gibt es eine Art Caching in der Class.getResource () - Version? Was mich zu der Annahme veranlasste, dass dies die Erzeugung einiger Jaspisberichte ist: Wir verwenden getClass (). GetResource ("/ aDocument.jrxml"), um die Jaspis-XML-Datei abzurufen. Eine binäre Jaspis-Datei wird dann im selben Verzeichnis erstellt. getClass (). getResource ("/ aDocument.jasper") kann es nicht finden, obwohl es Dokumente auf derselben Ebene (der Eingabedatei) eindeutig finden kann. Hier hat sich ClassLoader.getResource () als hilfreich erwiesen, da anscheinend kein Caching der Verzeichnisliste verwendet wird. Aber ich kann keine Dokumentation dazu finden.
Oligofren
2
@oligofren: Hmm ... Ich würde nicht erwarten, dass Class.getResource () dort Caching macht ...
Jon Skeet
1
@ JonSkeet warum this.getClass().getClassLoader().getResource("/");null zurückgeben? Es sollte nicht dasselbe sein wiethis.getClass().getClassLoader().getResource(".");
Asif Mushtaq
@UnKnown: Ich denke, Sie sollten wahrscheinlich eine neue Frage dazu stellen.
Jon Skeet
22

Der erste Aufruf sucht relativ zur .classDatei, während der letztere relativ zum Klassenpfadstamm sucht.

Um solche Probleme zu beheben, drucke ich die URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Aaron Digulla
quelle
3
Ich denke, "classloader root" wäre genauer als "classpath root" - nur um wählerisch zu sein.
Jon Skeet
4
Beide können nach "absoluten Pfaden" suchen, wenn dem Dateinamen "/"
oligofren vom
2
Interessant ... Ich stoße auf einen Fall, in dem getClass (). GetResource ("/ someAbsPath") eine URL vom Typ /path/to/mylib.jar!/someAbsPath und getClass () zurückgibt. GetClassLoafer (). GetResource (". / someAbsPath ") return null ... Also" root of classloader "scheint kein sehr gut definierter Begriff zu sein ...
Pierre Henry
8
@PierreHenry: getClassLoader().getResource("/...")kehrt immer zurück null- der Klassenladeprogramm entfernt den führenden nicht /aus dem Pfad, sodass die Suche immer fehlschlägt. Nur getClass().getResource()Griffe einen Start /als absoluter Pfad relativ zu dem Classpath.
Aaron Digulla
17

Musste es in den Spezifikationen nachschlagen:

Die getResource () - Dokumentation der Klasse gibt den Unterschied an:

Diese Methode delegiert den Aufruf an ihren Klassenladeprogramm, nachdem diese Änderungen am Ressourcennamen vorgenommen wurden: Wenn der Ressourcenname mit "/" beginnt, bleibt er unverändert. Andernfalls wird der Paketname nach der Konvertierung von "" dem Ressourcennamen vorangestellt. zu "/". Wenn dieses Objekt vom Bootstrap-Loader geladen wurde, wird der Aufruf an ClassLoader.getSystemResource delegiert.

Bernd Elkemann
quelle
2
Haben Sie Informationen darüber, ob die Verzeichnisliste auch zwischengespeichert wird? Dies war der Hauptunterschied zwischen den beiden Methoden, wenn Sie zuerst eine Eingabedatei nachschlagen und dann eine Datei mit derselben im selben Verzeichnis erstellen. Die Class-Version hat es nicht gefunden, die ClassLoader-Version (beide mit "/file.txt").
Oligofren
11

Alle diese Antworten hier sowie die Antworten in dieser Frage legen nahe, dass das Laden absoluter URLs wie "/foo/bar.properties" von class.getResourceAsStream(String)und gleich behandelt wird class.getClassLoader().getResourceAsStream(String). Dies ist NICHT der Fall, zumindest nicht in meiner Tomcat-Konfiguration / Version (derzeit 7.0.40).

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

Entschuldigung, ich habe absolut keine befriedigende Erklärung, aber ich denke, dass Tomcat schmutzige Tricks und seine schwarze Magie mit den Klassenladern macht und den Unterschied verursacht. Ich habe es class.getResourceAsStream(String)in der Vergangenheit immer benutzt und hatte keine Probleme.

PS: Ich stellte auch dies über hier

Tim Büthe
quelle
Dieses Verhalten scheint ein Fehler in Tomcat zu sein, der in Version 8 behoben wurde. Ich habe in meiner Antwort zu dieser Frage
LordOfThePigs
2

Class.getResourceswürde die Ressource vom Klassenladeprogramm abrufen, das das Objekt lädt. While ClassLoader.getResourcewürde die Ressource mit dem angegebenen Klassenladeprogramm abrufen.

lwpro2
quelle
0

Ich habe versucht, aus input1.txt zu lesen, das sich in einem meiner Pakete befand, zusammen mit der Klasse, die versucht hat, es zu lesen.

Folgendes funktioniert:

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

System.out.println(fileName);

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

Der wichtigste Teil war das Aufrufen, getPath()wenn Sie den richtigen Pfadnamen im String-Format wünschen. NICHT VERWENDEN,toString() da dadurch zusätzlicher Formatierungstext hinzugefügt wird, der den Dateinamen vollständig angibt (Sie können es versuchen und den Ausdruck sehen).

Verbrachte 2 Stunden damit, dies zu debuggen ... :(

Kevin Lee
quelle
Was ist mit Class.getResourceAsStream () ?
Lu55
Ressourcen sind keine Dateien. Sie könnten nicht von der JAR oder WAR - Datei entpackt werden, und wenn kein FileReaderoder FileInputStreamnicht zugangs sie verwendet werden können. Die Antwort ist nicht richtig.
Marquis von Lorne