我想知道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()
相同。但显然不是。请问有人可以为我提供一些启示吗?
答案 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#getResource
和 ClassLoader#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();