==背景== 我正在尝试为桌面java应用程序实现自动更新功能,该应用程序旨在使用某种管理程序来决定从哪里加载应用程序(即我们是否尝试加载新版本或者我们跟上当前的情况)。为此我创建了使用URLClassLoader的bootstrapper。
==情境== 我有以下文物:
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上我可以使用缓存的类但不能加载新的类。
有什么想法解决这个问题吗?
答案 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?
}
}
删除此代码后,问题就消失了。