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

时间:2011-07-07 09:59:34

标签: java classloader getresource

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

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

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

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

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

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

Class.getResource()上的API docs

  

查找资源   用一个给定的名字。规则   搜索与a相关的资源   给定的类是由实现的   定义类的类加载器。   此方法委托给此对象   类加载器。如果这个对象是   由bootstrap类加载器加载,   方法委托给   ClassLoader.getSystemResource(java.lang.String中)。

对我来说,这是“Class.getResource真正调用它自己的类加载器的getResource()”。这与执行getClass().getClassLoader().getResource()相同。但显然不是。请问有人可以为我提供一些启示吗?

9 个答案:

答案 0 :(得分:227)

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");

答案 1 :(得分:21)

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

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

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

答案 2 :(得分:16)

不得不在规格中查找:

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

  

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

答案 3 :(得分:10)

这里的所有这些答案以及this question中的答案都表明加载绝对网址,例如“/foo/bar.properties”按class.getResourceAsStream(String)和{{1}处理}。情况并非如此,至少在我的Tomcat配置/版本(目前为7.0.40)中没有。

class.getClassLoader().getResourceAsStream(String)

对不起,我绝对没有令人满意的解释,但我猜tomcat会用类加载器做肮脏的技巧和他的黑魔法并造成差异。我过去总是使用MyClass.class.getResourceAsStream("/foo/bar.properties"); // works! MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work! 并且没有任何问题。

PS:我也在here

上发布了这个帖子

答案 4 :(得分:4)

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

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

<强>然而 我正在开发一个项目,其中包含几个相互依赖的maven模块和Web项目。我正在使用IntelliJ作为我的IDE来编译和运行Web项目。

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

答案 5 :(得分:2)

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

答案 6 :(得分:1)

从 Java 9 开始,在模块路径上运行时 ClassLoader#getResource 存在缺陷。因此,我永远不会在新代码中使用 ClassLoader#getResource

如果您的代码位于命名模块中,并且您使用 ClassLoader#getResource,则即使资源位于同一模块中,您的代码也可能无法检索资源。这非常令人惊讶行为。

我自己经历过这种情况,并对 Class#getResourceClassLoader#getResource 之间的这种差异感到非常惊讶。但是,根据javadoc,它完全是指定的行为:

<块引用>

此外,除了资源名称以“.class”结尾的特殊情况外,该方法只会在无条件打开包时(即使调用者此方法与资源在同一模块中).

Javadoc(强调我的)

答案 7 :(得分:0)

我尝试从我的一个包中的input1.txt读取,并尝试读取它。

以下作品:

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

System.out.println(fileName);

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

如果您想要String格式的正确路径名,最重要的部分是调用getPath()请勿使用toString() ,因为它会添加一些额外的格式化文本,这些文本将完全取消文件名(您可以尝试并查看打印输出)。

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

答案 8 :(得分:0)

另一种更有效的方法是使用@Value

@Value("classpath:sss.json")
private Resource resource;

然后你就可以通过这种方式获取文件

File file = resource.getFile();