我有一个库,是商业产品的插入式扩展。该产品已有几年历史,使用的是Sun的JAXB-2.0库。
我的库使用JAXB,我有一个非常奇怪的错误。 What might cause "JAXBElement Does not have a no-arg default constructor"?
我发现这是由旧库中的错误引起的。我可以预先加载已加载的类以强制我的扩展使用类的“正确”版本吗?
编辑:随着我的插件我正在打包JAXB-2.2.5(在我的插件的类路径中引用),但我怀疑现有的JVM已经加载了2.0版本的类,引起了我的心痛。
答案 0 :(得分:2)
每个Java Class
都由ClassLoader
加载。一旦加载了类,就不可能从类加载器中卸载这个类。 删除类的唯一可能性是松开对此类加载器已加载的类的所有实例的所有引用,以及对类加载器本身的引用即可。
类加载器按层次结构组织。默认情况下,每个类加载器首先要求其父类加载器加载一个类。仅当父类加载器无法加载类时,原始类加载器才会尝试这样做。您可以覆盖此行为以加载JAXB类。
虽然这并不容易:假设您有一个加载了类加载器 A 的类MyObject
。如果使用类加载器 B 加载相同的类,则为MyObject.class != MyObject.class
加载两个类。这也会导致MyObject o = getMyObjectFromOtherClassLoader()
抛出ClassCastException
。
Stackoverflow提供an example of a child first class loader。唯一剩下的就是在类加载器激活之前运行应用程序而不加载类。这个,你可以这样做(未验证的代码):
class StartupWrapper {
public static void main(String[] args) throws Exception {
Class.forName("com.company.Main", true,
new ParentLastURLClassLoader(Arrays.asList(new URL("./new-JAXB.jar"))))
.getMethod("main", String[].class).invoke(null, args);
}
}
此包装器将使用首先考虑您的库的 child-first 类加载器启动类public static void main(String[])
的正常应用程序的com.company.Name
方法。确保在显式加载之前未加载com.company.Main
。 (因此反思的方法。)
您使用的是像Maven这样的构建工具吗?你不能明确地排除旧库的依赖吗?遗留应用程序可能会遇到与您的应用程序与旧JAXB类似的问题。