如何使加载的jar使用自己的资源而不是其加载器?

时间:2014-10-14 04:05:24

标签: java url jar resources classloader

摘要: 当我从另一个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

非常感谢任何帮助!提前致谢!

1 个答案:

答案 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(),但不要在那个引用我。