摘要: 当我从另一个jar加载并运行一个jar并且加载的jar尝试访问其内部的资源时,它无法找到这些资源。我相信这是因为它正在查看加载它而不是自身的jar。我该如何解决这个问题?
更详细的问题:
我正在尝试以编程方式加载jar文件 - 让我们调用它" Server" - 通过我自己的Java程序进入Java虚拟机 - 让我们称之为" ServerAPI" - 并使用扩展和一些其他技巧来修改Server的行为并与之交互。 ServerAPI依赖于Server,但如果Server不存在,ServerAPI仍然必须能够从网站运行和下载Server。
为了避免因ServerAPI加载而导致的错误而不满足Server的依赖关系,我已经制作了一个启动器 - 让我们调用它" Launcher" - 用于下载服务器并根据需要设置ServerAPI,然后加载Server和ServerAPI,然后运行ServerAPI。
我已经达到了Launcher成功加载Server和ServerAPI的程度。但是,当我尝试通过ServerAPI运行Server时,会出现错误,因为Server无法找到某些资源。资源位于Server jar中,因为它们始终存在。
我有一个想法,也许,因为资源路径似乎都是相对的,Java正在最初加载并运行整个程序的jar中搜索这些资源,即Launcher。为了测试这一点,我从服务器jar中获取了一些缺少的资源,并将它们放在Launcher jar中的相同路径中。与失踪资源相关的错误消失了。
那么,我需要一种方法来确保在该jar中找到服务器中的代码加载的所有资源,而不是让Java在Launcher jar中搜索它们。将资产复制到Launcher jar中可以用于我的测试,但是对于最终产品来说这将是不方便且容易出错的,更不用说它会增加文件大小这一事实,因为有大量资源需要复制和制作在某些情况下,相同文件的多个副本可能会导致错误。
我已经想过的想法不起作用:
我无法将资源复制到Launcher jar中,因为它会增加Launcher的大小;它进一步使程序复杂化,并且有时可能导致错误,有时Server自己运行,有时它使用Launcher通过ServerAPI运行;并且它不允许ServerAPI适用于多个版本的Server。
我无法将所有资源从服务器移到Launcher中以消除冗余和歧义,因为出于复杂的法律原因,我无法修改Server。但是,我可以自由地修改Launcher或ServerAPI,因为我创建了它们。
我无法修改资源调用的路径。我使用ServerAPI修改服务器行为的技巧在代码的这些部分不起作用,即使它们这样做,也有很多调用这么多资源,所以需要很长时间才能找到它们并修改它们。
我已就这个问题做过研究:
This question是我发现的唯一与此问题相关的问题,但由于上述段落中提到的原因,我无法修改对资源的每次调用。
//编辑:
服务器的资源调用似乎主要使用格式CLASS.class.getResourceAsStream("/RESOURCE")
。这有帮助吗?
// END EDIT
//编辑2:
我尝试使用Thread.currentThread().setContextClassLoader()
将ClassLoader
设置为我用来加载服务器的那个,希望因为我使用用于加载服务器的ClassLoader
所有资源路径现在都可以相对于服务器。不幸的是,这不起作用。
// END EDIT
//编辑3:
到目前为止,我正在初始化用于将服务器加载为Launcher自己的ClassLoader
的{{1}}的父级,因此我尝试将父级设置为 {{ 1}} 而不是让它作为一个独立的ClassLoader
,希望这会打破它与Launcher的联系。这也不起作用。
// END EDIT
非常感谢任何帮助!提前致谢!
答案 0 :(得分:1)
通过一些技巧和hackish修复,我设法使它工作!
我所做的不是使用标准URLClassLoader来加载Server和ServerAPI中的类,而是使用了我自己的扩展URLClassLoader的自定义ClassLoader类。它基本上以相同的方式工作,除了我覆盖getResource()
方法以预先添加(附加到开头)jar文件的绝对路径URL,基本上强制它在其特定的jar文件中查找资源而不是制作它相对于ClassLoader。
该重写方法的代码如下所示:
@Override
public URL getResource(String path) {
/* eliminate the "/" at the beginning if there is one to avoid
conflicts with the "!/" at the end of the URL */
if (path.startsWith("/"))
path.substring(1);
/* prepend "jar:" to indicate that the URL points to a jar file and use
"!/" as the separator to indicate that the resource being loaded is
inside the jar file */
String URL_string = "jar:" + getURLs()[0] + "!/" + path;
try {
return new URL(URL_string);
} catch (MalformedURLException exception) {
System.out.println("There was something wrong with the URL representing this resource!");
System.out.println("URL=\"" + URL_string + "\"");
exception.printStackTrace();
return null;
}
}
我必须以艰难的方式学习这一点并自己提出修复方法;我希望这个答案可以帮助其他人解决同样的问题!
注意:这也修复了Class.getResource()
方法,因为Class.getResource()
只是简单地预先设置" /"如果那里还没有,那么请致电ClassLoader.getResource()
。它似乎也可以修复Class(Loader).getResourceAsStream()
,但不要在那个引用我。