Quelle est la différence entre Class.getResource () et ClassLoader.getResource ()?

194

Je me demande quelle est la différence entre Class.getResource()et ClassLoader.getResource()?

edit: Je veux surtout savoir si une mise en cache est impliquée au niveau du fichier / répertoire. Comme dans "les listes de répertoires sont-elles mises en cache dans la version de classe?"

L'AFAIK devrait essentiellement faire de même, mais ce n'est pas le cas:

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

J'ai découvert cela en jouant avec du code de génération de rapport qui crée un nouveau fichier à WEB-INF/classes/partir d'un fichier existant dans ce répertoire. Lorsque j'utilisais la méthode de Class, je pouvais trouver des fichiers qui étaient là lors du déploiement en utilisant getClass().getResource(), mais en essayant de récupérer le fichier nouvellement créé, j'ai reçu un objet nul. La navigation dans le répertoire montre clairement que le nouveau fichier est là. Les noms de fichiers ont été précédés d'une barre oblique comme dans "/myFile.txt".

La ClassLoaderversion de getResource()d'autre part a trouvé le fichier généré. D'après cette expérience, il semble qu'il y ait une sorte de mise en cache de la liste des répertoires en cours. Ai-je raison, et si oui, où est-ce documenté?

Depuis les documents de l' API surClass.getResource()

Recherche une ressource avec un nom donné. Les règles de recherche des ressources associées à une classe donnée sont implémentées par le chargeur de classe définissant la classe. Cette méthode délègue au chargeur de classe de cet objet. Si cet objet a été chargé par le chargeur de classe d'amorçage, la méthode délègue à ClassLoader.getSystemResource (java.lang.String).

Pour moi, cela se lit "Class.getResource appelle vraiment son propre chargeur de classe getResource ()". Ce qui reviendrait à le faire getClass().getClassLoader().getResource(). Mais ce n'est évidemment pas le cas. Quelqu'un pourrait-il me fournir un éclairage sur cette question?

oligofren
la source

Réponses:

6

Pour répondre à la question de savoir s'il y a une mise en cache.

J'ai approfondi ce point en exécutant une application Java autonome qui chargeait en continu un fichier à partir du disque à l'aide de la méthode getResourceAsStream ClassLoader. J'ai pu modifier le fichier et les modifications ont été immédiatement reflétées, c'est-à-dire que le fichier a été rechargé à partir du disque sans mise en cache.

Cependant: je travaille sur un projet avec plusieurs modules maven et projets web qui ont des dépendances les uns des autres. J'utilise IntelliJ comme mon IDE pour compiler et exécuter les projets Web.

J'ai remarqué que ce qui précède ne semblait plus vrai, la raison étant que le fichier que j'étais en train de charger est maintenant cuit dans un bocal et déployé sur le projet Web dépendant. Je ne l'ai remarqué qu'après avoir essayé de modifier le fichier dans mon dossier cible, en vain. Cela donnait l'impression que la mise en cache était en cours.

mchlstckl
la source
J'ai également utilisé Maven et IntelliJ, c'est donc la réponse avec un environnement qui correspond le plus au mien et a une explication raisonnable pour la question # 2.
oligofren
249

Class.getResourcepeut prendre un nom de ressource "relatif", qui est traité par rapport au package de la classe. Vous pouvez également spécifier un nom de ressource "absolu" à l'aide d'une barre oblique. Les chemins d'accès aux ressources du chargeur de classe sont toujours considérés comme absolus.

Les éléments suivants sont donc essentiellement équivalents:

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

Et il en va de même (mais ils sont différents des précédents):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Jon Skeet
la source
Belle réponse avec des exemples clairs. Bien que le message soit en fait destiné à obtenir des réponses à deux questions, je vois maintenant que la deuxième question est en quelque sorte cachée. Je ne sais pas trop comment / si je dois mettre à jour le message pour refléter cela, mais ce que j'aimerais savoir en second lieu est le suivant (commentaire suivant):
oligofren
2
Existe-t-il une sorte de mise en cache dans la version Class.getResource ()? Ce qui m'a fait croire que c'est la génération de certains rapports jasper: Nous utilisons getClass (). GetResource ("/ aDocument.jrxml") pour récupérer le fichier jasper xml. Un fichier jasper binaire est alors produit dans le même répertoire. getClass (). getResource ("/ aDocument.jasper") n'est pas en mesure de le trouver, bien qu'il puisse clairement trouver des documents au même niveau (le fichier d'entrée). C'est là que ClassLoader.getResource () s'est avéré utile, car il semble qu'il n'utilise pas la mise en cache de la liste des répertoires. Mais je ne trouve pas de documentation à ce sujet.
oligofren
2
@oligofren: Hmm ... Je ne m'attendrais pas à ce que Class.getResource () fasse de la mise en cache là-bas ...
Jon Skeet
1
@JonSkeet pourquoi this.getClass().getClassLoader().getResource("/");retourner null? Il ne devrait pas être le même quethis.getClass().getClassLoader().getResource(".");
Asif Mushtaq
@UnKnown: Je pense que vous devriez probablement poser une nouvelle question à ce sujet.
Jon Skeet
22

Le premier appel recherche par rapport au .classfichier tandis que le dernier recherche par rapport à la racine du chemin de classe.

Pour déboguer des problèmes comme ça, j'imprime l'URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Aaron Digulla
la source
3
Je pense que "root classloader" serait plus précis que "root classpath" - juste pour être pointilleux.
Jon Skeet
4
Les deux peuvent rechercher des "chemins absolus" si le nom du fichier est ajouté par "/"
oligofren
2
Intéressant ... Je frappe un cas où getClass (). GetResource ("/ someAbsPath") renvoie une URL dans le type /path/to/mylib.jar!/someAbsPath et getClass (). GetClassLoafer (). GetResource (" / someAbsPath ") return null ... Donc," root of classloader "ne semble pas être une notion très bien définie ...
Pierre Henry
8
@PierreHenry: getClassLoader().getResource("/...")renvoie toujours null- le chargeur de classe ne supprime pas le début /du chemin, donc la recherche échoue toujours. getClass().getResource()Gère uniquement un début /comme chemin absolu par rapport au chemin de classe.
Aaron Digulla
17

J'ai dû chercher dans les spécifications:

La documentation getResource () de la classe fait la différence:

Cette méthode délègue l'appel à son chargeur de classe, après avoir apporté ces modifications au nom de la ressource: si le nom de la ressource commence par "/", il reste inchangé; sinon, le nom du package est ajouté au nom de la ressource après la conversion de "." à "/". Si cet objet a été chargé par le chargeur d'amorçage, l'appel est délégué à ClassLoader.getSystemResource.

Bernd Elkemann
la source
2
Avez-vous des informations sur la mise en cache de la liste des répertoires? C'était la principale différence entre les deux méthodes lors de la première recherche d'un fichier d'entrée, puis de la création d'un fichier en utilisant celui-ci dans le même répertoire. La version de classe ne l'a pas trouvée, la version de ClassLoader l'a fait (à la fois en utilisant "/file.txt").
oligofren
11

Toutes ces réponses ici, ainsi que les réponses à cette question , suggèrent que le chargement d'URL absolues, comme "/foo/bar.properties", est traité de la même manière par class.getResourceAsStream(String)et class.getClassLoader().getResourceAsStream(String). Ce n'est PAS le cas, du moins pas dans ma configuration / version Tomcat (actuellement 7.0.40).

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

Désolé, je n'ai absolument aucune explication satisfaisante, mais je suppose que Tomcat fait des tours sales et sa magie noire avec les chargeurs de classe et cause la différence. J'ai toujours utilisé class.getResourceAsStream(String)dans le passé et je n'ai eu aucun problème.

PS: j'ai aussi posté ça ici

Tim Büthe
la source
Ce comportement semble être un bogue dans Tomcat qui a été corrigé dans la version 8. J'ai ajouté un paragraphe à ce sujet dans ma réponse à cette question
LordOfThePigs
2

Class.getResourcesrécupérerait la ressource par le chargeur de classe qui charge l'objet. While ClassLoader.getResourcerécupérerait la ressource à l'aide du chargeur de classe spécifié.

lwpro2
la source
0

J'ai essayé de lire depuis input1.txt qui se trouvait dans l'un de mes packages avec la classe qui essayait de le lire.

Les oeuvres suivantes:

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

System.out.println(fileName);

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

La partie la plus importante était d'appeler getPath()si vous voulez le nom de chemin correct au format String. NE PAS UTILISERtoString() car il ajoutera du texte de mise en forme supplémentaire qui MESSERA TOTALEMENT le nom de fichier (vous pouvez l'essayer et voir l'impression).

J'ai passé 2 heures à déboguer ceci ... :(

Kevin Lee
la source
Qu'en est-il de Class.getResourceAsStream () ?
Lu55
Les ressources ne sont pas des fichiers. Ils pourraient ne pas être déballés à partir du fichier JAR ou WAR, et sinon un FileReaderou FileInputStreamne peuvent pas être utilisés pour accéder eux. La réponse n'est pas correcte.
Marquis de Lorne le