【问题标题】:Why would ClassLoader.getResourceAsStream() return null?为什么 ClassLoader.getResourceAsStream() 会返回 null?
【发布时间】:2014-05-23 03:49:17
【问题描述】:

故意破坏以下代码以识别 NullPointerException 的来源,这本来应该很简单,但结果让我抓狂:

Properties properties = new Properties();
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader = currentThread.getContextClassLoader();
InputStream propertiesStream = contextClassLoader.getResourceAsStream("resource.properties");
if (propertiesStream != null) {
  properties.load(propertiesStream);
  // TODO close the stream
} else {
  // Properties file not found!
}

我收到“找不到属性文件!”错误,即 contextClassLoader.getResourceAsStream("resource.properties"); 返回 null

这是一个基于 CXF 的客户端,我验证了“resource.properties”文件在客户端的 jar 所在(并运行)的当前目录中.

我还通过包含以下诊断代码验证了绝对路径:

            File file = new File("resource.properties");
            System.out.println(file.getAbsolutePath());

绝对路径指向客户端的jar所在的位置。

我还尝试找出类加载器的上下文,使用:

  System.out.println(Thread.currentThread().getContextClassLoader());

而是一些目录结构as demonstrated here,我得到的只是:

com.simontuffs.onejar.JarClassLoader@1decdec

为什么 ClassLoader.getResourceAsStream() 会返回 null?

我错过了什么?

【问题讨论】:

  • 因为命名资源不能通过 CLASSPATH 获得。
  • 哪个类路径?我打印出classpath env var,它包括当前目录(“。”)。我很困惑。

标签: java jar classpath cxf onejar


【解决方案1】:

我解开了这个谜。

解决的关键是嵌入一些诊断日志propertiesStream 为空时:

String classpath = System.getProperty("java.class.path");
LOG.info("CLASSPATH: " + classpath);
ClassLoader loader = MyClientMain.class.getClassLoader();
System.out.println("ClassLoader resource path: " + loader.getResource("resource.properties"));                    

所以当我使用原版运行时

contextClassLoader.getResourceAsStream("resource.properties")

我收到空指针条件,打印:

  INFO: CLASSPATH: myproj.one-jar.jar
  ClassLoader resource path: null

.

然后我开始怀疑与 “jar 中的 jar” 相关的东西,因为这就是 com.simontuffs.onejar 本质上所做的(即将我的项目的 jar 包装在里面一个包含所有其他库 jar 的 jar),所以我用 7-Zip 打开 myproj.one-jar.jar 并记下“resource.properties”的完整(绝对)路径:

myproj.one-jar.jar\main\myproj.jar\webapp\WEB-INF\classes\resource.properties

.

所以我将getResource("resource.properties")修改为:

 getResource("/main/myproj.jar/webapp/WEB-INF/classes/resource.properties")

没有解决问题,但在空指针条件下打印了以下内容:

INFO: CLASSPATH: myproj.one-jar.jar
ClassLoader resource path: jar:file:/myproj.one-jar.jar!/main/myproj.jar!//main/myproj.jar/webapp/WEB-INF/classes/resource.properties

.

然后……上帝的干预降临到我身上,我有洞察力(我发誓没有阅读任何可能暗示这一点的文档!)我应该改用这条路径:

 getResource("/webapp/WEB-INF/classes/resource.properties")

还有,瞧!它有效。

哇哦。

【讨论】:

  • 故事的寓意:虽然从 相同的确切路径加载资源的 WAR 文件只需要文件名(即 /webapp/WEB-INF/classes/ 是隐含的并自动确定),但 JAR 文件需要整个路径。
  • 2 1/2 年后,我在至少 20 个其他地方阅读了这篇文章后来到这里,但都没有解决我的问题。但你做到了:在字符串前面添加“/webapp”解决了我的问题。我没有在任何地方看到记录,所以我不知道你的洞察力是如何得出的,但这让我在被卡住 1.5 小时后再次行动起来。谢谢!!
【解决方案2】:

正如 EJP 指出的那样,这意味着资源不能通过该特定类加载器的类路径获得(不同的类加载器可以有不同的类路径)。

由于类加载器是JarClassLoader,它只能加载包含在 jar 文件中的资源。它不会看到与 jar 文件位于同一目录中的文件。

【讨论】:

  • 谢谢你,这正是我最初所做的。我将文件放在项目的src/main/resourceswebapp/WEB-INF/classes 目录中,正确包含在JAR 文件中。无济于事。我错过了什么? com.simontuffs.onejar.JarClassLoader 是否有可能以某种方式修改类路径?如果是这样,我如何找到事实上的类路径?
  • 检查 resource.properties 文件是否包含在 jar 文件中(如果您希望使用该类加载器来加载它)。也尝试将其加载为/resource.properties(如果它在 jar 的根目录中)。
  • 是的,它包含在(两个位置,至少在调试阶段),我尝试了resource.properties/resource.properties。无济于事。一些邪恶的事情正在发生。我怀疑某些特定于“simontuffs”的东西......当客户端应用程序在运行时看到它时,如何打印当前的类路径?
  • 没有单一的类路径。这一切都取决于ClassLoader。如果它自己找不到它,它可以委托给它的父级ClassLoader,但我怀疑JarClassLoader(如URLClassLoader)不会委托给它的父级。您可以使用getPackages() 方法查看类加载器知道哪些包,然后确保资源在正确子目录的 jar 中(即,如果类加载器知道包 com.foo.bar,则该文件应该是在 com/foo/bar/resource.properties 的 jar 中)。
  • 我刚刚使用了Package.getPackages(),它打印了世界......即数百个包,但没有路径或路径提示。仍在尝试找出正确的子目录。非常感谢有关如何解决此问题的其他建议。
猜你喜欢
  • 2013-08-09
  • 2013-06-05
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
相关资源
最近更新 更多