Eclipse/OSGI、Java11、JAXB和类加载器

Eclipse/OSGI、Java11、JAXB和类加载器,java,eclipse,jaxb,osgi,classloader,Java,Eclipse,Jaxb,Osgi,Classloader,我有两个Java模块,A和B。A提供了一个核心模型,其中包含JAXB注释和帮助器类,用于创建JAXB内容(创建上下文、编组、解组等)。B提供了其他类,这些类通过@xmlanyement(lax=true)包含在模型中,因此必须添加到JAXB上下文中 这在普通Java-B的classloader中运行良好,它可以看到所有相关的类,并可以使用以下内容实例化JAXB上下文: JAXBContext.newInstance(RootFromA.class, RootFromB.class) 现在我也在

我有两个Java模块,A和B。A提供了一个核心模型,其中包含JAXB注释和帮助器类,用于创建JAXB内容(创建上下文、编组、解组等)。B提供了其他类,这些类通过@xmlanyement(lax=true)包含在模型中,因此必须添加到JAXB上下文中

这在普通Java-B的classloader中运行良好,它可以看到所有相关的类,并可以使用以下内容实例化JAXB上下文:

JAXBContext.newInstance(RootFromA.class, RootFromB.class)
现在我也在尝试OSGI(B是Eclipse插件,A是核心库,普通Java命令行模块C也将使用它)。经过多次尝试和错误,我已经设法让A和B通过OSGI包导入来查看JAXB API和实现。问题在于,如上所述调用newInstance似乎使用了JAXB API的类加载器,而不是RootFromA的类加载器,当然也不是RootFromB的类加载器。因此,它甚至无法看到JAXB实现,并抱怨找不到ContextFactory类

我已通过调用不同版本的newInstance解决了此问题:

JAXBContext.newInstance(
  RootFromA.class.getPackageName()
    + ":" + RootFromB.class.getPackageName(),
  RootFromB.class.getClassLoader())
我不喜欢这样,有两个原因:

  • 我的“客户机”代码(B是A中JAXB助手的客户机)必须手动提供合适的类加载器
  • 我必须在列出上下文类的所有引用包中提供jaxb.index文件,即使我的代码完全知道它们,并且实际上从类中获取包名
  • (1)可能没有任何方法,因为只有B知道完整的类集合,并且可以决定谁的类加载器能够看到所有的类。但我担心,一旦我添加扩展模块C和D,通过Eclipse扩展点连接到B,并为JAXB上下文提供额外的类,我可能会遇到更多的麻烦——B的类加载器能够看到这些吗

    但我真的找到了一种方法来摆脱(2)所需的静态索引文件。全套上下文类是动态的,在纯Java中由ServiceLoader决定,在Eclipse中由扩展点决定。在这两种情况下,我都可以直接访问应该属于上下文的全套类,因此考虑必须手动添加JAXBEXCEL文件到每个包冗余,因此潜在的和不必要的错误来源。
    我错过什么了吗?有没有“更好”的方法?为什么没有一个newInstance方法来接受一组上下文类和一个类加载器呢?

    看来我找到了一个解决方案。我的问题是为了两个不同的目的处理类加载器:

  • JAXB用来实例化上下文的类加载器
  • 我的各种模型类的类装入器
  • 如上所述,(1)可以在JAXBContext.newInstance()中指定为参数,但仅当将模型指定为包名而不是单个模型类时。这意味着JAXB必须自己查找类,而它唯一可以使用的类加载器是(1)——如果模型类分布在多个bundle中,它就看不到所有的模型类

    另一个线程()告诉我,JAXB默认使用线程上下文类加载器来定位上下文实现。将其设置为可以看到实现的类加载器(例如,我自己的bundle的类加载器)就足以让JAXB找到自己的实现,这让我可以自由地使用类数组调用我的首选版本newInstance()。由于这些类已经加载,JAXB可以按原样使用它们,而不必关心它们的不同类加载器

    简言之:

    Class[] myClasses = getModelClasses(); // gather all model classes, which may have different classloaders
    Thread thread = Thread.currentThread();
    ClassLoader originalClassLoader = thread.getContextClassLoader();
    thread.setContextClassLoader(getClass().getClassLoader()); // my own bundle's classloader
    JAXBContext context = JAXBContext.newInstance(myClasses);
    thread.setContextClassLoader(originalClassLoader); // reset context classloader
    

    上下文类加载器操作可以包装在一个自动关闭的文件中,这样我就可以简单地将JAXBContext实例化包装在try-with-resources块中。

    Update:我已经在测试项目中添加了一个扩展插件,上面的第1点确实令人担忧。我可以通过Platform.getBundle().loadClass()从扩展点加载贡献的模型类并实例化它们,但我不能将它们添加到JAXB上下文中,因为它们显然来自不同的类加载器,在创建JAXBContext时,我只能指定一个类加载器和包名。多年来,我一直在纯Java中使用模块化JAXB结构——在Eclipse中肯定有合适的方法来实现这一点吗?