URLClassLoader无法满足来自新线程的类加载请求

时间:2014-12-24 07:54:08

标签: java classloader

==背景== 我正在尝试为桌面java应用程序实现自动更新功能,该应用程序旨在使用某种管理程序来决定从哪里加载应用程序(即我们是否尝试加载新版本或者我们跟上当前的情况)。为此我创建了使用URLClassLoader的bootstrapper。

==情境== 我有以下文物:

  • bootstrapper.jar
  • app.jar
  • LIB / *。罐

bootstrapper.jar 拥有自己的main()方法,而MANIFEST文件引用入口点为

Main-Class: ru.skarpushin.projects.bootstrapper.Bootstrapper

所以我可以通过调用命令行

来运行它
javaw -jar bootsrapper.jar

app.jar 这是主应用程序(基于Spring的应用程序),其中包含对第三方库(位于lib文件夹中)的依赖数,并从manifest.mf引用

Class-Path: lib/balloontip-1.2.3-SNAPSHOT.jar lib/commons-codec-1.6.ja
 r lib/commons-lang3-3.1.jar lib/commons-logging-1.1.1.jar lib/fluent-
 hc-4.2.1.jar lib/gson-2.2.2.jar lib/guava-11.0.2.jar lib/log4j-1.2.16
 .jar lib/org.springframework.asm-3.1.2.RELEASE.jar lib/org.springfram

lib / * .jar - 一堆依赖

==用户类加载器的方式是==

classLoader = new URLClassLoader(new URL[] { new File("app.jar").toURI().toURL() }, Thread.currentThread()
        .getContextClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);
Class entryPointClass = classLoader.loadClass(entryPoint);
Method mainMethod = entryPointClass.getMethod("main", String[].class);
mainMethod.invoke(null, (Object) args);

==问题== 这适用于某些课程,而不适用于其他课程。问题是为什么以及如何解决这个问题?

看起来它与线程有某种关系。 让我们调用Thread1 - 应用程序启动时的主线程以及构造URLCalssLoader的位置。 所有在Thread1上加载类的请求都成功完成,无论它们的位置如何(无论是app.jar还是来自libs)。 所有在新创建的线程上加载类的请求(让我们称之为ThreadN)都失败了。

我发现的有趣的事情是: - 假设ThreadN无法加载ClassA - 如果我更改代码以便强制ClassA从Thread1加载 - 那么ThreadN上就不会有错误 - 我检查过 - Thread1和ThreadN的上下文类加载器 - 是相同的

所以看起来在ThreadN上我可以使用缓存的类但不能加载新的类。

有什么想法解决这个问题吗?

1 个答案:

答案 0 :(得分:0)

不幸的是,这是导致此行为的代码。

Bootstrapper.main()方法已启动;它打开了URLClassLoader,加载了应用程序入口点并调用它。所有这些都在主线程中完成。问题是这是桌面java应用程序 - 一旦显示主框架,则代码被解除阻塞并退出bootstrapper.main方法(当应用程序仍在执行时)。

但是在bootstrapper.main中我确实有finally子句,我在URLClassLoader上显式调用.close()方法,强制它只使用缓存的类,不加载新的类。

} finally {
    try {
        classLoader.close();
    } catch (IOException e) {
        // failed to close. don't care. Should we?
    }
}

删除此代码后,问题就消失了。