【问题标题】:Can I pre-empt the class path? [duplicate]我可以抢占类路径吗? [复制]
【发布时间】:2013-10-30 20:16:01
【问题描述】:

我有一个库,它是商业产品的插入式扩展。该产品已有数年历史,使用 Sun 的 JAXB-2.0 库。

我的库使用 JAXB,但我遇到了一个非常奇怪的错误。 What might cause "JAXBElement Does not have a no-arg default constructor"?

我发现这是由旧库中的错误引起的。我可以抢占加载的类以强制我的扩展使用“正确”版本的类吗?

编辑:连同我的插件,我正在打包 JAXB-2.2.5(在我的插件的类路径中引用),但我怀疑现有的 JVM 已经加载了该类的 2.0 版本,导致我心痛.

【问题讨论】:

  • 您可以使用自定义类加载器隐藏加载的类路径,该类加载器不委托给其父级,而是尝试自行加载尽可能多的类。这样,您至少可以找出是否是原因。
  • @raphw 我对 JVM 的理解比我的理解要深一些。你能稍微扩展一下吗?
  • 为了这个目的,评论字段有点短。请参阅下面的答案。

标签: java classpath


【解决方案1】:

每个 Java Class 都由 ClassLoader 加载。一旦加载了一个类,就不可能从类加载器中卸载这个类。 摆脱一个类的唯一可能性是释放对这个类加载器已加载的类的所有实例的所有引用以及对类加载器本身的引用

类加载器按层次结构组织。默认情况下,每个类加载器首先要求其父类加载器加载一个类。只有当父类加载器无法加载某个类时,原始类加载器才会尝试这样做。您可以覆盖此行为以加载您的 JAXB 类。

但这并不容易:假设你有一个类MyObject,它是用类加载器A加载的。如果您使用类加载器 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 类加载器启动您的普通应用程序的com.company.Name 类的public static void main(String[]) 方法。确保在显式加载之前不加载 com.company.Main。 (因此是反射方法。)

您使用的是 Maven 之类的构建工具吗?你不能明确排除旧图书馆的依赖吗?旧版应用程序在使用新 JAXB 时可能会遇到与您的应用程序使用旧版 JAXB 时类似的问题。

【讨论】:

  • 最终我用更新的版本就地替换了库。然而,这是一个 hack,这是“正确”的解决方案;很高兴您在这里分享了这些信息。
猜你喜欢
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
相关资源
最近更新 更多