我想知道 Class.getResource()
和 ClassLoader.getResource()
之间有什么区别?
编辑:我特别想知道文件/目录级别是否涉及任何缓存。正如“目录列表是否缓存在类版本中?”
AFAIK 本质上应该执行相同的操作,但事实并非如此:
getClass().getResource()
getClass().getClassLoader().getResource()
我在摆弄一些在 中创建新文件的报告生成代码时发现了这一点WEB-INF/classes/
来自该目录中的现有文件。当使用 Class 的方法时,我可以使用 getClass().getResource() 找到部署时存在的文件,但是当尝试获取新创建的文件时,我收到了一个 null 对象。浏览目录清楚地显示新文件就在那里。文件名前面带有正斜杠,如“/myFile.txt”中所示。
另一方面,getResource()
的 ClassLoader
版本确实找到了生成的文件。从这次经验来看,似乎正在对目录列表进行某种缓存。我说得对吗?如果是的话,这个记录在哪里?
来自 API 文档 在 Class.getResource()
上
找到资源
具有给定的名称。规则为
搜索与某个相关的资源
给定的类由
定义类的类加载器。
该方法委托给该对象的
类加载器。如果这个物体是
由引导类加载器加载,
该方法委托给
ClassLoader.getSystemResource(java.lang.String)。
对我来说,这意味着“Class.getResource 实际上是在调用它自己的类加载器的 getResource()”。这与执行 getClass().getClassLoader().getResource()
相同。但显然不是。有人可以为我提供一些关于此事的说明吗?
I wonder what the difference is between Class.getResource()
and ClassLoader.getResource()
?
edit: I especially want to know if any caching is involved on file/directory level. As in "are directory listings cached in the Class version?"
AFAIK the following should essentially do the same, but they are not:
getClass().getResource()
getClass().getClassLoader().getResource()
I discovered this when fiddling with some report generation code that creates a new file in WEB-INF/classes/
from an existing file in that directory. When using the method from Class, I could find files that were there at deployment using getClass().getResource()
, but when trying to fetch the newly created file, I recieved a null object. Browsing the directory clearly shows that the new file is there. The filenames were prepended with a forward slash as in "/myFile.txt".
The ClassLoader
version of getResource()
on the other hand did find the generated file. From this experience it seems that there is some kind of caching of the directory listing going on. Am I right, and if so, where is this documented?
From the API docs on Class.getResource()
Finds a resource
with a given name. The rules for
searching resources associated with a
given class are implemented by the
defining class loader of the class.
This method delegates to this object's
class loader. If this object was
loaded by the bootstrap class loader,
the method delegates to
ClassLoader.getSystemResource(java.lang.String).
To me, this reads "Class.getResource is really calling its own classloader's getResource()". Which would be the same as doing getClass().getClassLoader().getResource()
. But it is obviously not. Could someone please provide me with some illumination into this matter?
发布评论
评论(9)
Class.getResource 可以采用“相对”资源名称,该名称是相对于类的包进行处理的。或者,您可以使用前导斜杠指定“绝对”资源名称。类加载器资源路径始终被认为是绝对的。
所以以下内容基本上是等价的:
这些也是(但它们与上面不同):
Class.getResource
can take a "relative" resource name, which is treated relative to the class's package. Alternatively you can specify an "absolute" resource name by using a leading slash. Classloader resource paths are always deemed to be absolute.So the following are basically equivalent:
And so are these (but they're different from the above):
第一个调用相对于
.class
文件进行搜索,而后者则相对于类路径根进行搜索。为了调试此类问题,我打印了 URL:
The first call searches relative to the
.class
file while the latter searches relative to the classpath root.To debug issues like that, I print the URL:
必须在规范中查找它:
Class.getResource(字符串资源)
ClassLoader.getResource(字符串资源)
类的 getResource() - 文档说明了差异:
Had to look it up in the specs:
Class.getResource(String resource)
ClassLoader.getResource(String resource)
Class's getResource() - documentation states the difference:
这里的所有这些答案,以及这个问题中的答案,都建议加载绝对 URL,例如“/foo/bar.html”。
class.getResourceAsStream(String)
和class.getClassLoader().getResourceAsStream(String)
处理相同的属性”。事实并非如此,至少在我的 Tomcat 配置/版本(当前为 7.0.40)中并非如此。抱歉,我绝对没有令人满意的解释,但我猜想 tomcat 对类加载器做了肮脏的把戏和他的黑魔法并导致了差异。我过去一直使用
class.getResourceAsStream(String)
并且没有遇到任何问题。PS:我还在此处发布了此内容
All these answers around here, as well as the answers in this question, suggest that loading absolute URLs, like "/foo/bar.properties" treated the same by
class.getResourceAsStream(String)
andclass.getClassLoader().getResourceAsStream(String)
. This is NOT the case, at least not in my Tomcat configuration/version (currently 7.0.40).Sorry, I have absolutely no satisfying explanation, but I guess that tomcat does dirty tricks and his black magic with the classloaders and cause the difference. I always used
class.getResourceAsStream(String)
in the past and haven't had any problems.PS: I also posted this over here
回答是否有缓存的问题。
我通过运行一个独立的 Java 应用程序进一步研究了这一点,该应用程序使用 getResourceAsStream ClassLoader 方法连续从磁盘加载文件。我能够编辑该文件,并且更改会立即反映出来,即,该文件是从磁盘重新加载的,而不进行缓存。
但是:
我正在开发一个包含多个相互依赖的 Maven 模块和 Web 项目的项目。我使用 IntelliJ 作为 IDE 来编译和运行 Web 项目。
我注意到上面的内容似乎不再成立,原因是我正在加载的文件现在已烘焙到 jar 中并部署到依赖的 Web 项目中。我只是在尝试更改目标文件夹中的文件后才注意到这一点,但无济于事。这使得看起来好像正在进行缓存。
To answer the question whether there is any caching going on.
I investigated this point further by running a stand-alone Java application that continuously loaded a file from disk using the getResourceAsStream ClassLoader method. I was able to edit the file, and the changes were reflected immediately, i.e., the file was reloaded from disk without caching.
However:
I'm working on a project with several maven modules and web projects that have dependencies on each other. I'm using IntelliJ as my IDE to compile and run the web projects.
I noticed that the above seemed to no longer hold true, the reason being that the file that I was being loaded is now baked into a jar and deployed to the depending web project. I only noticed this after trying to change the file in my target folder, to no avail. This made it seem as though there was caching going on.
从 Java 9 开始,在模块路径上运行时,
ClassLoader#getResource
存在一个陷阱。因此,我永远不会在新代码中使用 ClassLoader#getResource 。如果您的代码位于命名模块中,并且您使用
ClassLoader#getResource
,则即使资源位于同一模块中,您的代码也可能无法检索资源。非常令人惊讶的行为。我自己也经历过这一点,并且对 Class#getResource 和 ClassLoader#getResource 之间的差异感到非常惊讶。然而,这完全是根据 javadoc 指定的行为:
Since Java 9 there is a pitfall with
ClassLoader#getResource
when running on the module path. Because of this, I would never useClassLoader#getResource
in new code.If your code is in a named module, and you use
ClassLoader#getResource
, your code can fail to retrieve a resource even if the resource is in the same module. This is very surprising behavior.I experienced this myself and was very surprised as to this difference between
Class#getResource
andClassLoader#getResource
. However, it is entirely specified behavior according to the javadoc:Class.getResources 将通过加载对象的类加载器检索资源。而
ClassLoader.getResource
将使用指定的类加载器检索资源。Class.getResources
would retrieve the resource by the classloader which load the object. WhileClassLoader.getResource
would retrieve the resource using the classloader specified.我尝试从我的一个包中的 input1.txt 以及试图读取它的类一起读取。
以下工作:
最重要的部分是调用
getPath()< /code> 如果您想要字符串格式的正确路径名。 不要使用
toString()
,因为它会添加一些额外的格式化文本,这会完全搞乱文件名(您可以尝试一下并查看打印输出)。花了 2 个小时调试这个...:(
I tried reading from input1.txt which was inside one of my packages together with the class which was trying to read it.
The following works:
The most important part was to call
getPath()
if you want the correct path name in String format. DO NOT USEtoString()
because it will add some extra formatting text which will TOTALLY MESS UP the fileName (you can try it and see the print out).Spent 2 hours debugging this... :(
另一种更有效的方法是使用@Value
,然后您就可以通过这种方式获取文件
Another more efficient way to do is just use @Value
and after that you can just get the file this way