Class.getResource()和ClassLoader.getResource()有什么区别?

oli*_*ren 186 java classloader getresource

我想知道Class.getResource()和之间的区别是什么ClassLoader.getResource()

编辑:我特别想知道文件/目录级别是否涉及任何缓存.如"在类版本中缓存的目录列表?"

以下AFAIK应该基本上做同样的事情,但它们不是:

getClass().getResource() 
getClass().getClassLoader().getResource()
Run Code Online (Sandbox Code Playgroud)

我在摆弄一些报告生成代码时发现了这一点,该代码WEB-INF/classes/从该目录中的现有文件创建了一个新文件.当使用Class中的方法时,我可以找到部署时使用的文件getClass().getResource(),但在尝试获取新创建的文件时,我收到了一个null对象.浏览目录会清楚地显示新文件存在.文件名前面带有正斜杠,如"/myFile.txt"中所示.

另一方面,ClassLoader版本getResource()确实找到了生成的文件.根据这种经验,似乎存在某种目录列表的缓存.我是对的,如果是的话,这会记录在哪里?

API文档Class.getResource()

查找具有给定名称的资源.搜索与给定类关联的资源的规则由类的定义类加载器实现.此方法委托给此对象的类加载器.如果此对象由引导类加载器加载,则该方法委托给ClassLoader.getSystemResource(java.lang.String).

对我来说,这是"Class.getResource真正调用它自己的类加载器的getResource()".这和做的一样getClass().getClassLoader().getResource().但显然不是.有人可以请我为此事提供一些启示吗?

Jon*_*eet 236

Class.getResource可以采用"相对"资源名称,该名称相对于类的包进行处理.或者,您可以使用前导斜杠指定"绝对"资源名称.类加载器资源路径始终被视为绝对路径.

所以以下几乎是等价的:

foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");
Run Code Online (Sandbox Code Playgroud)

这些也是如此(但它们与上述不同):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Run Code Online (Sandbox Code Playgroud)

  • 在Class.getResource()版本中是否存在某种缓存?是什么让我相信这是一些jasper报告的产生:我们使用getClass().getResource("/ aDocument.jrxml")来获取jasper xml文件.然后在*same*目录中生成二进制jasper文件.getClass().getResource("/ aDocument.jasper")无法找到它,尽管它可以清楚地找到同一级别的文档(输入文件).这是ClassLoader.getResource()证明有用的地方,因为它似乎不使用目录列表的缓存.但是我找不到关于此的文档. (2认同)
  • @oligofren:嗯......我不希望*Class.getResource()在那里做任何缓存...... (2认同)

Aar*_*lla 21

第一个调用相对于.class文件进行搜索,而后者则相对于类路径根进行搜索.

要调试这样的问题,我打印URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Run Code Online (Sandbox Code Playgroud)

  • @PierreHenry:`getClassLoader().getResource("/ ...")`总是返回`null` - 类加载器不会从路径中删除前导`/`,因此查找总是失败.只有`getClass().getResource()`将起始`/`作为相对于类路径的绝对路径处理. (8认同)
  • 如果文件名前缀为"/",则两者都可以搜索"绝对路径" (4认同)
  • 我认为"classloader root"比"classpath root"更准确 - 只是为了挑剔. (3认同)
  • 有意思......我遇到的情况是getClass().getResource("/ someAbsPath")返回类型为/path/to/mylib.jar!/someAbsPath和getClass()的URL .getClassLoafer().getResource("/someAbsPath")返回null ...所以"类加载器的根"似乎不是一个非常明确的概念...... (2认同)

Ber*_*ann 16

不得不在规格中查找:

Class的getResource() - 文档说明了区别:

在对资源名称进行这些更改后,此方法将调用委托给其类加载器:如果资源名称以"/"开头,则它保持不变; 否则,在转换"."之后,包名称将被添加到资源名称之前.至 "/".如果此对象由引导加载程序加载,则该调用将委派给ClassLoader.getSystemResource.

  • 你有没有关于它是否缓存目录列表的信息?这是两种方法在首次查找输入文件,然后在同一目录中使用该文件创建文件时的主要区别.Class版本没有找到它,ClassLoader版本(两者都使用"/file.txt"). (2认同)

Tim*_*the 10

这里所有这些答案,以及这个问题的答案,都表明加载绝对URL,如"/foo/bar.properties"处理相同的class.getResourceAsStream(String)class.getClassLoader().getResourceAsStream(String).情况并非如此,至少在我的Tomcat配置/版本(目前为7.0.40)中没有.

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!
Run Code Online (Sandbox Code Playgroud)

对不起,我绝对没有令人满意的解释,但我想tomcat会用类加载器做肮脏的技巧和他的黑魔法并造成差异.我以前总是用过class.getResourceAsStream(String),没有任何问题.

PS:我也在这里发布了这个


mch*_*ckl 6

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

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

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

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