eclipse/osgi、java11、jaxb和类加载器

hivapdat  于 2021-07-08  发布在  Java
关注(0)|答案(1)|浏览(441)

我有两个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包导入查看jaxbapi和实现。问题是,如上所述调用newinstance似乎使用了jaxbapi的类加载器,而不是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的classloader能够看到这些吗?
但我真的找到了一种方法来摆脱(2)所需的静态索引文件。全套上下文类是动态的,在纯java中由serviceloader决定,在eclipse中由扩展点决定。在这两种情况下,我都可以直接访问应该属于上下文的完整类集,因此我认为必须手动将jaxb.index文件添加到每个包中是多余的,因此是一个潜在的、不必要的错误源。
我错过什么了吗?有没有更好的方法?为什么没有一个newinstance方法来接受一组上下文类和一个类加载器呢?

9q78igpj

9q78igpj1#

看来我找到了解决办法。我的问题是为了两个不同的目的处理类加载器:
jaxb用来示例化上下文的类加载器
我的各种模型类的类装入器
如上所述,(1)可以在jaxbcontext.newinstance()中指定为参数,但仅当将模型指定为包名而不是单个模型类时。这意味着jaxb必须自己查找类,它唯一可以使用的类加载器是(1)——如果模型类分布在多个bundle中,它就看不到所有的模型类。
另一个线程(为什么jaxb在apachefelix内部运行时找不到我的jaxb.index?)告诉我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

上下文类加载器操作的东西可以 Package 在一个autocloseable中,这样我就可以简单地将jaxbcontext示例化 Package 在try with resources块中。

相关问题