对于您传递的fileName 的解释方式存在细微差别。基本上,您有 2 种不同的方法:ClassLoader.getResourceAsStream() 和 Class.getResourceAsStream()。这两种方法会以不同的方式定位资源。
在Class.getResourceAsStream(path) 中,路径被解释为您从中调用它的类的包的本地路径。例如,调用String.class.getResourceAsStream("myfile.txt") 将在您的类路径中的以下位置查找文件:"java/lang/myfile.txt"。如果您的路径以/ 开头,那么它将被视为绝对路径,并且将从类路径的根目录开始搜索。所以调用String.class.getResourceAsStream("/myfile.txt") 将在你的类路径./myfile.txt 中查看以下位置。
ClassLoader.getResourceAsStream(path) 将所有路径视为绝对路径。所以调用String.class.getClassLoader().getResourceAsStream("myfile.txt") 和String.class.getClassLoader().getResourceAsStream("/myfile.txt") 都会在你的类路径中的以下位置查找文件:./myfile.txt。
每当我在这篇文章中提到一个位置时,它可能是您文件系统本身中的一个位置,也可能是相应 jar 文件中的一个位置,具体取决于您从中加载资源的 Class 和/或 ClassLoader。
在您的情况下,您是从应用程序服务器加载类,因此您应该使用Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) 而不是this.getClass().getClassLoader().getResourceAsStream(fileName)。 this.getClass().getResourceAsStream() 也可以。
阅读this article 了解有关该特定问题的更多详细信息。
Tomcat 7 及以下用户的警告
这个问题的一个答案表明,我的解释对于 Tomcat 7 似乎是不正确的。我试图环顾四周,看看为什么会出现这种情况。
所以我查看了 Tomcat 的 WebAppClassLoader 的源代码,了解了几个版本的 Tomcat。 findResource(String name)(最终负责生成请求资源的 URL)的实现在 Tomcat 6 和 Tomcat 7 中几乎相同,但在 Tomcat 8 中有所不同。
在版本 6 和 7 中,实现不会尝试规范化资源名称。这意味着在这些版本中,classLoader.getResourceAsStream("/resource.txt") 可能不会产生与 classLoader.getResourceAsStream("resource.txt") 事件相同的结果,尽管它应该(因为 Javadoc 指定了)。 [source code]
但在版本 8 中,资源名称被规范化以保证资源名称的绝对版本是使用的那个。因此,在 Tomcat 8 中,上述两个调用应该总是返回相同的结果。 [source code]
因此,在 Tomcat 8 之前的版本上使用 ClassLoader.getResourceAsStream() 或 Class.getResourceAsStream() 时必须格外小心。而且您还必须记住,class.getResourceAsStream("/resource.txt") 实际上调用了 classLoader.getResourceAsStream("resource.txt")(前导 @987654349 @ 被剥离)。