我不知道Class.getResource()ClassLoader.getResource()之间有什么区别?

edit:我特别想知道文件/目录级别上是否涉及任何缓存。就像“是否在Class版本中缓存目录列表?”中的以下内容基本相同,但实际上它们不是:

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


我在处理一些报表生成代码时发现了这一点,该代码从该目录中的现有文件在WEB-INF/classes/中创建了一个新文件。使用Class中的方法时,可以使用getClass().getResource()找到部署时存在的文件,但是当尝试获取新创建的文件时,我收到了一个空对象。浏览目录清楚地表明新文件在那里。文件名前面有一个正斜杠,如“ /myFile.txt”。

另一方面,ClassLoadergetResource()版本确实找到了生成的文件。从这种经验看来,目录列表存在某种形式的缓存。

Class.getResource()上的API文档中找到文档吗?


查找具有给定名称的资源

搜索与给定类相关的资源的规则由定义该类的
类加载器实现。
此方法委托给该对象的
类加载器。如果此对象是由引导类加载器加载的,则该方法将委托给
ClassLoader.getSystemResource(java.lang.String)。


对我来说,这是“ Class.getResource实际上正在调用其自己的类加载器的getResource()”。这与执行getClass().getClassLoader().getResource()相同。但这显然不是。有人可以为我提供一些启发性的东西吗?

#1 楼

为了回答是否存在任何缓存问题。

我运行了一个独立的Java应用程序,进一步研究了这一点,该应用程序使用getResourceAsStream ClassLoader方法从磁盘连续加载文件。我能够编辑文件,并且更改会立即反映出来,例如,文件已从磁盘重新加载而不进行缓存。

但是:
我正在一个有多个Maven的项目中工作相互依赖的模块和Web项目。我正在使用IntelliJ作为我的IDE来编译和运行Web项目。

我注意到上面的内容似乎不再成立,原因是现在正在加载的文件已经被烘焙放入jar并部署到相关的Web项目中。我只是在尝试更改目标文件夹中的文件后才注意到这一点,但无济于事。这使得好像正在进行缓存。

评论


我也使用过Maven和IntelliJ,所以这是最接近我的环境并且对问题#2有合理解释的答案。

–寡核苷酸
2014年1月10日上午9:55

#2 楼

Class.getResource可以采用“相对”资源名称,相对于类的程序包而言。或者,您可以使用斜杠指定“绝对”资源名称。类加载器资源路径始终被视为绝对路径。

因此,以下内容基本上是等效的:

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


这些也是(但它们都是与上面的不同):

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


评论


答案清晰,示例清晰。尽管该帖子实际上旨在获得两个问题的答案,但我现在看到第二个问题有点隐藏。非常不确定我应该如何/是否应该更新帖子以反映这一点,但是我想知道的第二件事是(下一条评论):

–寡核苷酸
2011年7月7日在16:04

Class.getResource()版本中是否存在某种类型的缓存?使我相信这是一些jasper报告的生成:我们使用getClass()。getResource(“ / aDocument.jrxml”)来获取jasper xml文件。然后,在同一目录中生成一个二进制jasper文件。尽管getClass()。getResource(“ / aDocument.jasper”)可以清楚地找到同一级别的文档(输入文件),却无法找到它。这就是ClassLoader.getResource()证明有用的地方,因为它似乎不使用目录列表的缓存。但是我找不到关于此的文档。

–寡核苷酸
2011年7月7日在16:08

@oligofren:嗯...我不希望Class.getResource()在那里做任何缓存...

–乔恩·斯基特(Jon Skeet)
2011年7月7日在16:18

@JonSkeet为什么this.getClass()。getClassLoader()。getResource(“ /”);返回空值?它不应与this.getClass()。getClassLoader()。getResource(“。”);相同。

–阿西夫·穆斯塔克(Asif Mushtaq)
18年2月16日在22:20

@UnKnown:我想您可能应该问一个新的问题。

–乔恩·斯基特(Jon Skeet)
18年2月17日在8:06

#3 楼

第一个调用相对于.class文件进行搜索,而第二个相对于类路径根目录进行搜索。

要调试类似的问题,我打印URL:

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


评论


我认为“ classloader根目录”比“ classpath根目录”更准确-只是要挑剔。

–乔恩·斯基特(Jon Skeet)
2011年7月7日在10:14

如果文件名以“ /”开头,两者都可以搜索“绝对路径”

–寡核苷酸
2011年7月7日在15:59

有趣的是……我遇到了这样一种情况:getClass()。getResource(“ / someAbsPath”)返回类型为/path/to/mylib.jar!/someAbsPath和getClass()。getClassLoafer()。getResource(“ / someAbsPath“)返回null ...因此,” classloader的根目录“似乎不是一个很好定义的概念...

–皮埃尔·亨利(Pierre Henry)
13年4月16日在14:35

参见stackoverflow.com/questions/13269556/…

–皮埃尔·亨利(Pierre Henry)
13年4月16日在14:36

@PierreHenry:getClassLoader()。getResource(“ / ...”)始终返回null-类加载器不会从路径中删除前导/,因此查找始终会失败。只有getClass()。getResource()将开始/作为相对于类路径的绝对路径进行处理。

–亚伦·迪古拉(Aaron Digulla)
2013年4月16日14:43



#4 楼

必须在规格中查找它:


Class.getResource(String resource)
ClassLoader.getResource(String resource)

Class的getResource( )-文档指出了不同之处:


此方法在对资源名称进行了这些更改之后,将调用委托给其类加载器:如果资源名称以“ /”开头,则为不变否则,将包名称转换为“。”之后的资源名称之前。至 ”/”。如果此对象是由引导加载程序加载的,则该调用将委派给ClassLoader.getSystemResource。


评论


您是否还有是否缓存目录列表的任何信息?当首先查找输入文件,然后在同一目录中使用该文件创建文件时,这是两种方法之间的主要区别。 Class版本找不到它,ClassLoader版本找到了(两者都使用“ /file.txt”)。

–寡核苷酸
2011年7月7日在16:22

#5 楼

此处的所有这些答案以及该问题的答案都表明,通过class.getResourceAsStream(String)class.getClassLoader().getResourceAsStream(String)处理加载绝对URL(如“ /foo/bar.properties”)的方式相同。并非如此,至少在我的Tomcat配置/版本(当前为7.0.40)中不是这样。

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


对不起,我绝对没有令人满意的解释,但是我猜想tomcat会在类加载器上做肮脏的把戏和他的黑魔法,并造成差异。我过去一直使用class.getResourceAsStream(String),并且没有任何问题。

PS:我也在这里发布了此内容

评论


此行为似乎是Tomcat的错误,该错误已在版本8中修复。我在关于此问题的答案中添加了与此相关的段落

–LordOfThePigs
2014年7月6日下午0:31

#6 楼

Class.getResources将由加载对象的类加载器检索资源。虽然ClassLoader.getResource将使用指定的类加载器检索资源。

#7 楼

我尝试从我的一个软件包中的input1.txt以及试图读取它的类中读取。

以下作品:

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

System.out.println(fileName);

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


最重要的部分是如果您要使用String格式的正确路径名,请致电getPath()。请勿使用toString(),因为它会添加一些额外的格式化文本,从而使fileName总计减少(您可以尝试一下并查看打印出来的内容)。

花了2个小时调试了此...:(

评论


那Class.getResourceAsStream()呢?

–Lu55
18年1月15日在15:34

资源不是文件。它们可能不会从JAR或WAR文件中解压缩,否则,不能使用FileReader或FileInputStream来访问它们。答案不正确。

–user207421
18年7月3日在2:30