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?"
Autant que je sache, ce qui suit devrait essentiellement faire la même chose, mais ce n’est pas le cas:
getClass().getResource()
getClass().getClassLoader().getResource()
J'ai découvert cela en manipulant du code de génération de rapport qui crée un nouveau fichier dans WEB-INF/classes/
à partir d'un fichier existant de ce répertoire. Lors de l'utilisation de la méthode de Class, je pouvais trouver les fichiers présents lors du déploiement à l'aide de getClass().getResource()
, mais lors de la tentative d'extraction du fichier nouvellement créé, j'ai reçu un objet null. Parcourir 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 version ClassLoader
de getResource()
a par contre 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 de répertoires en cours. Ai-je raison, et si oui, où est-ce documenté?
Depuis le docs de l'API sur Class.getResource()
Trouve une ressource avec un prénom. Les règles de recherche des ressources associées à une classe donnée sont implémentées par le chargeur de classe qui définit la classe. Cette méthode délègue au chargeur de classes de cet objet. Si cet objet a été chargé par le chargeur de classe bootstrap, la méthode délègue à ClassLoader.getSystemResource (Java.lang.String).
Pour moi, cela lit "Class.getResource appelle vraiment getResource () de son propre chargeur de classes". Ce qui équivaudrait à faire getClass().getClassLoader().getResource()
. Mais ce n'est évidemment pas. Est-ce que quelqu'un pourrait m'éclairer sur ce sujet?
Pour répondre à la question de savoir s'il y a une mise en cache en cours.
J'ai approfondi cette question en exécutant une application autonome Java qui chargeait en permanence un fichier à partir d'un disque à l'aide de la méthode getResourceAsStream ClassLoader. J'ai pu éditer 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 dépendent l'un de l'autre. J'utilise IntelliJ en tant que mon IDE pour compiler et exécuter les projets Web.
J'ai remarqué que ce qui précède semblait ne plus être vrai, la raison étant que le fichier que j'étais en train d'être chargé est maintenant cuit dans un pot et déployé dans le projet Web dépendant. Je ne l'ai remarqué qu'après avoir essayé de changer le fichier dans mon dossier cible, mais en vain. Cela donnait l'impression que la mise en cache était en cours.
Class.getResource
peut prendre un nom de ressource "relatif", qui est traité relativement au paquet de la classe. Sinon, vous pouvez spécifier un nom de ressource "absolu" en utilisant une barre oblique. Les chemins de ressources Classloader sont toujours considérés comme absolus.
Donc, les éléments suivants sont fondamentalement équivalents:
foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");
Et ainsi sont-ils (mais ils sont différents de ce qui précède):
foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Le premier appel effectue une recherche relative au fichier .class
tandis que le dernier effectue une recherche relative à la racine du chemin de classe.
Pour résoudre ces problèmes, j'imprime l'URL:
System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
J'ai dû le regarder dans les spécifications:
La documentation getResource () de la classe indique la différence:
Cette méthode délègue l'appel à son chargeur de classes, après avoir apporté ces modifications au nom de la ressource: si le nom de la ressource commence par "/", il est inchangé; sinon, le nom du paquet est ajouté au nom de la ressource après la conversion de "." à "/". Si cet objet a été chargé par le chargeur bootstrap, l'appel est délégué à ClassLoader.getSystemResource.
Toutes ces réponses ici, ainsi que les réponses dans cette question , suggèrent que le chargement d’URL absolues, comme "/foo/bar.properties", a été 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 fait la différence. J'ai toujours utilisé class.getResourceAsStream(String)
par le passé et je n'ai rencontré aucun problème.
PS: J'ai aussi posté ceci sur ici
Class.getResources
récupérerait la ressource par le chargeur de classes qui charge l'objet. Bien que ClassLoader.getResource
récupère la ressource à l'aide du chargeur de classe spécifié.
J'ai essayé de lire 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 bon chemin d’accès au format String. NE PAS UTILISER toString()
car cela ajoutera du texte de formatage supplémentaire qui modifiera TOTALEMENT le nom du fichier (vous pouvez l’essayer et voir l’impression).
J'ai passé 2 heures à déboguer ceci ... :(