可执行 jar 中 Class.getResource() 和 ClassLoader.getResource() 的奇怪行为
Posted
技术标签:
【中文标题】可执行 jar 中 Class.getResource() 和 ClassLoader.getResource() 的奇怪行为【英文标题】:Strange behavior of Class.getResource() and ClassLoader.getResource() in executable jar 【发布时间】:2012-10-27 11:59:03 【问题描述】:我从What is the difference between Class.getResource() and ClassLoader.getResource()?了解到 并从自己的代码中,即
getClass().getResource("/path/image.png")
等同于
getClass().getClassLoader().getResource("path/image.png")
发帖Cannot read an image in jar file 显示使用问题
getClass().getClassLoader().getResource("path/image.png")
在可执行的 jar 文件中返回 null,而
getClass().getResource("/path/image.png")
返回正确的 URL。
Since Class.getResource()
在删除前导斜杠后委托给ClassLoader.getResource()
,我希望这些调用是相同的,但显然它们不是在这种情况下。即使将特殊的类加载器附加到特定类,每次调用它仍然应该是相同的,再次导致相同的行为。
所以,问题是:是否有任何明显的情况会导致以下情况 代码第一次调用返回 null 但第二次调用返回正确的 URL?
package com.example;
import java.net.URL;
public class ResourceTest
public void run()
URL iconUrl1 = getClass().getClassLoader().getResource("path/image.png");
System.out.println("ClassLoader.getResource(\"path/image.png\"): " + iconUrl1);
URL iconUrl2 = getClass().getResource("/path/image.png");
System.out.println("Class.getResource(\"/path/image.png\"): " + iconUrl2);
public static void main(String[] args)
ResourceTest app = new ResourceTest();
app.run();
【问题讨论】:
您是否重现了这种行为,或者这个问题只是基于您链接的问题?尝试提供 SSCCE,然后人们将能够回答这个问题。 不幸的是,它只是基于我链接的那个 - 我无法重现它。如果可以的话,我肯定会发布一个 SSCCE :),我希望通过这个问题获得一些反馈,以便能够编写重现此类问题的代码。我已经使用调试器进入了 getClass().getResource(),并且看到了上面第一个链接中提到的算法,但我不知道在哪些情况下会发生链接问题中描述的行为。 请添加一个工作示例。我查看了 java.lang 类源和 javadoc 中的信息,但我得到的信息与您已经提到的相同。 @gyabraham 我添加了一个工作样本。知道如何打破它吗? (听起来像是一个奇怪的问题;)) 问:知道如何破解它吗?答:是的。如果你想要一个“null”而不是另一个,你所要做的就是将你的“类”文件放在与“类加载器根”不同的目录中。换句话说,您所要做的就是引入一个包(无论如何您都应该这样做)。我的示例在我的机器上执行此操作:1/2 案例是“SUCCESS”,1/2 是“NULL”。我正在使用 JDK 1.6。我知道当您运行 我的相同代码 时,您会得到 all “SUCCESS” 而没有“NULL”。问:对吗? 【参考方案1】:我以为这个问题已经被问及回答了!
What is the difference between Class.getResource() and ClassLoader.getResource()?
getClass().getResource()
搜索相对于 .class 文件,而getClass().getClassLoader().getResource()
搜索相对于 类路径根目录。
如果这里有 SSCCE,我不明白为什么没有
1) 在 .jar 中显示目录组织,然后...
2) 考虑包装
问:What is the difference between Class.getResource() and ClassLoader.getResource()?(以及它引用的链接)尚未回答什么(如果有的话)?
================================================ ============================
我仍然不确定有什么不清楚的地方,但这个例子可能会有所帮助:
/*
SAMPLE OUTPUT:
ClassLoader.getResource(/subdir/readme.txt): NULL
Class.getResource(/subdir/readme.txt): SUCCESS
ClassLoader.getResource(subdir/readme.txt): SUCCESS
Class.getResource(subdir/readme.txt): NULL
*/
package com.so.resourcetest;
import java.net.URL;
public class ResourceTest
public static void main(String[] args)
ResourceTest app = new ResourceTest ();
public ResourceTest ()
doClassLoaderGetResource ("/subdir/readme.txt");
doClassGetResource ("/subdir/readme.txt");
doClassLoaderGetResource ("subdir/readme.txt");
doClassGetResource ("subdir/readme.txt");
private void doClassLoaderGetResource (String sPath)
URL url = getClass().getClassLoader().getResource(sPath);
if (url == null)
System.out.println("ClassLoader.getResource(" + sPath + "): NULL");
else
System.out.println("ClassLoader.getResource(" + sPath + "): SUCCESS");
private void doClassGetResource (String sPath)
URL url = getClass().getResource(sPath);
if (url == null)
System.out.println("Class.getResource(" + sPath + "): NULL");
else
System.out.println("Class.getResource(" + sPath + "): SUCCESS");
这是相应的目录树。它恰好是一个 Eclipse 项目,但无论是 Eclipse、Netbeans ... 还是 .jar 文件,目录都是相同的:
C:.
├───.settings
├───bin
│ ├───com
│ │ └───so
│ │ └───resourcetest
│ └───subdir
└───src
├───com
│ └───so
│ └───resourcetest
└───subdir
打开的文件是“subdir/readme.txt”
附录 11/9/12:
嗨-
我从 github 逐字复制您的代码,重新编译并重新运行:
ClassLoader.getResource(/subdir/readme.txt): NULL
Class.getResource(/subdir/readme.txt): SUCCESS
ClassLoader.getResource(subdir/readme.txt): SUCCESS
Class.getResource(subdir/readme.txt): NULL
如果那不是你得到的输出......我很困惑。
不惜一切代价,我正在奔跑:
Eclipse Indigo(没关系)
在 IDE 内部运行(不管是文件系统还是 .jar,在 IDE 内部还是外部)
我的 JRE 是 1.6(如果有的话,这可能是大问题)
抱歉,我们无法解决我认为简单的问题:(
附录 11/21/12(安德烈亚斯):
由于最近没有关于这个问题的活动,我想总结一下我们的发现:
根据我们的普遍理解,上述问题的答案是:“不,不可能Class.getResource("/path/image.png")
返回有效URL,而ClassLoader.getResource("path/image.png")
返回null”:
我们非常清楚 ClassLoader.getResource() 和 Class.getResource() 之间的区别
我们的示例输出匹配,“SUCCESS”和“null”
样本输出符合我们的预期
结论:要么我们监督了某些事情,要么某些不同的事情导致链接问题中描述的“解决方案”起作用。我认为我们目前无法证明其中之一。
【讨论】:
我还没有看到答案的问题是:getClass().getResource("/path/image.png");
如何在一个可执行的 jar 文件中返回一个 URL 而getClass().getClassLoader().getResource("path/image.png");
返回 null (假设包装相同)。我的理解是“这是不可能的,至少在普通标准 JDK/JRE 环境中是不可能的”。在这种情况下,原始(链接)问题的 OP 将具有完全不同的根本问题。否则,请告诉我如何打包我的示例代码以实现此行为:)
您的样本完全有效,与我的理解和样本的输出没有区别。不过,问题是:有没有办法将它打包成一个可执行的 jar 文件,这样Class.getResource(/subdir/readme.txt)
返回SUCCESS
。但ClassLoader.getResource(subdir/readme.txt)
返回NULL
。
是的,该死!!!!这正是我的第一个示例所做的!!!!请查看示例输出 - 或自行运行。在您自己的目录或您自己的 .jar 中 - 它们完全等效!!!!请……看……再次!请记住:1)“包”意味着“目录路径”,2)加载器的“绝对路径”是一回事,3)“类”的“绝对路径”是,依赖于包,一个 不同的东西!!!!
@Andreas - 问:您看到示例如何为 Class.getResource(/subdir/readme.txt) 返回“SUCCESS”,为 ClassLoader.getResource(subdir/readme.txt) 返回“null” ?你自己试过了吗?使用 .jar 文件?
不,当我运行您的示例时,ClassLoader.getResource(subdir/readme.txt) 不会返回“null”,而是返回成功,请参阅pastebin.com/eMgAiLNy。对于两者,从文件系统或可执行 jar 运行。这也是您的示例代码中的注释所说的。有关完整的可运行示例,请参阅 github.com/afester/***/tree/master/getResource - build shell 脚本显示了 jar 文件是如何构建和执行的。以上是关于可执行 jar 中 Class.getResource() 和 ClassLoader.getResource() 的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章