215

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

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

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

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

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

另一方面,该ClassLoader版本确实找到了生成的文件。getResource()从这个经验来看,似乎对目录列表进行了某种缓存。我是对的,如果是这样,这在哪里记录?

API 文档开始Class.getResource()

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

对我来说,这读作“Class.getResource 真的在调用它自己的类加载器的 getResource()”。这与做getClass().getClassLoader().getResource(). 但显然不是。有人可以为我提供一些关于这件事的启示吗?

4

9 回答 9

280

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-07-07T10:03:59.750 回答
26

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

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

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
于 2011-07-07T10:02:16.027 回答
19

必须在规格中查找它:

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

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

于 2011-07-07T10:03:41.920 回答
12

这里的所有这些答案,以及这个问题中的答案,都表明加载绝对 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!

抱歉,我绝对没有令人满意的解释,但我猜想 tomcat 用类加载器做了肮脏的把戏和他的黑魔法并导致了差异。我以前一直用过class.getResourceAsStream(String),没有任何问题。

PS:我也发过这里

于 2013-11-19T11:04:49.860 回答
7

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

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

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

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

于 2012-05-15T08:49:01.323 回答
2

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

于 2014-08-14T09:12:47.433 回答
2

ClassLoader#getResource由于 Java 9在模块路径上运行时存在一个陷阱。因此,我永远不会ClassLoader#getResource在新代码中使用。

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

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

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

Javadoc(强调我的)

于 2021-04-04T01:32:36.273 回答
0

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

以下作品:

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

System.out.println(fileName);

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

getPath()如果您想要字符串格式的正确路径名,最重要的部分是调用。不要使用toString(),因为它会添加一些额外的格式文本,这会完全弄乱文件名(你可以试试看打印出来的)。

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

于 2014-03-11T13:34:15.900 回答
0

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

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

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

File file = resource.getFile();

于 2021-01-06T08:29:30.217 回答