Eclipse/OSGI,Java 11,JAXB和类加载器 [英] Eclipse/OSGI, Java 11, JAXB, and the Classloader

查看:114
本文介绍了Eclipse/OSGI,Java 11,JAXB和类加载器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个Java模块,A和B.A提供一个带有JAXB批注和帮助程序类的核心模型,用于创建JAXB东西(创建上下文,编组,解组等).B提供了包含在JAXB中的其他类.通过@XmlAnyElement(lax = true)建立模型,因此必须将其添加到JAXB上下文中.

I have two Java modules, A and B. A provides a core model with JAXB annotations and helper classes for creating doing JAXB stuff (creating the context, marshalling, unmarshalling, etc.) B provides additional classes that are included in the model via @XmlAnyElement(lax = true) and must therefore be added to the JAXB context.

这在纯Java中很好用-B的类加载器可以查看所有相关类,并可以使用以下方法实例化JAXB上下文:

This works fine in plain Java - B's classloader sees all the relevant classes and can instantiate the JAXB context with:

JAXBContext.newInstance(RootFromA.class, RootFromB.class)

现在,我正尝试使用OSGI(B是Eclipse插件,A是核心库,纯Java命令行模块C也将使用它).经过反复试验,我设法让A和B通过OSGI包导入来查看JAXB API和实现.问题在于,如上所述调用newInstance似乎使用的是JAXB API的类加载器,而不是RootFromA的类加载器,当然也不是RootFromB的一种.这样,它甚至无法看到JAXB实现,并抱怨找不到ContextFactory类.

Now I'm trying the same with OSGI (B is an Eclipse plug-in and A is the core library that will also be used by a plain Java command line module C). After much trial and error, I've managed to get A and B to see both the JAXB API and implementation via OSGI package imports. The problem is that calling newInstance as above seems to use the JAXB API's classloader, not the one of RootFromA, and certainly not the one of RootFromB. As such, it fails to even see the JAXB implementation and complains that it can't find the ContextFactory class.

我设法通过调用不同版本的newInstance来解决此问题:

I've managed to resolve this by calling a different version of newInstance:

JAXBContext.newInstance(
  RootFromA.class.getPackageName()
    + ":" + RootFromB.class.getPackageName(),
  RootFromB.class.getClassLoader())

我不喜欢这样做,有两个原因:

I don't like this, for two reasons:

  1. 我的客户"代码(B是A中JAXB helper的客户端)必须手动提供合适的类加载器.
  2. 我必须在列出上下文类的所有引用包中提供jaxb.in​​dex文件,即使我的代码完全了解它们并实际上从这些类中获取包名.

(1)可能没有办法,因为只有B知道完整的类集,并可以确定谁的类加载器可以看到所有类.我担心,一旦添加了通过Eclipse扩展点连接到B的扩展模块C和D并为JAXB上下文提供其他类,我可能会遇到更多的麻烦-B的类加载器能否看到那些?

There probably isn't any way around (1), because only B knows the full set of classes and can decide whose classloader is able to see all of them. I'm worried though that I might run into more trouble once I add extension modules C and D that hook into B via Eclipse extension points and provide additional classes for the JAXB context - will B's classloader be able to see those?

但是我真的会找到一种方法来摆脱(2)所需的静态索引文件.整个上下文类是动态的,由ServiceLoader在纯Java中确定,而在Eclipse由扩展点确定.在这两种情况下,我都可以直接访问应属于上下文的全套类,因此可以考虑将jaxb.in​​dex文件手动添加到每个程序包中是多余的,因此可能会导致不必要的错误.

But I'd really find a way to get rid of the static index files required for (2). The full set of context classes is dynamic and decided in plain Java by a ServiceLoader and in Eclipse by an extension point. In both cases I have direct access to the full set of classes that should belong to the context, and thus consider having to manually add jaxb.index files to each package redundant and therefore a potential and unnecessary source of error.

我错过了什么吗?是否有一个更精细"的解决方案?做到这一点的方法?为什么没有一个newInstance方法带有一组上下文类和一个类加载器呢?

Am I missing something? Is there a "nicer" way to do this? And why isn't there a newInstance method that takes a set of context classes and a classloader?

推荐答案

似乎我找到了解决方案.我的问题是必须出于两个不同的目的处理类加载器:

It seems I found a solution. My problem was having to deal with classloaders for two different purposes:

  1. JAXB用于实例化上下文的类加载器
  2. 各种模型类的类加载器

如上所述,可以将(1)指定为JAXBContext.newInstance()中的参数,但是仅当将模型指定为包名称而不是单个模型类时才可以.这意味着JAXB必须自己查找类,并且它唯一可以使用的类加载器是(1)-如果它们分散在多个包中,则无法看到所有模型类.

As described above, (1) can be specified as a parameter in JAXBContext.newInstance(), but only when specifying the model as package names instead of individual model classes. This means JAXB has to look up the classes on its own, and it the only classloader it can use to do that is (1) - which can't see all the model classes if they are spread across multiple bundles.

另一个线程(为什么可以在Apache Felix中运行时,JAXB找不到我的jaxb.in​​dex吗?)告诉我,JAXB默认使用线程上下文类加载器来定位上下文实现.将其设置为可以看到实现的类加载器(例如,我自己的捆绑包的实现)足以使JAXB找到自己的实现,这使我可以自由地使用类数组调用我的首选版本的newInstance().而且由于这些类已经被加载,因此JAXB可以按原样使用它们,而不必关心它们的不同类加载器.

Another thread (Why can't JAXB find my jaxb.index when running inside Apache Felix?) taught me that JAXB defaults to using the thread context classloader to locate the context implementation. Setting that to a classloader that sees the implementation (e.g. that of my own bundle) is sufficient to make JAXB find its own implementation, which leaves me free to call my preferred version of newInstance() with an array of classes. And since these classes have already been loaded, JAXB can use them as is and doesn't have to care about their different classloaders.

简而言之:

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

上下文类加载器的操作内容可以包装在AutoCloseable中,这使我可以将JAXBContext实例包装在try-with-resources块中.

The context classloader manipulation stuff can be wrapped in an AutoCloseable, allowing me to simply wrap the JAXBContext instantiation in a try-with-resources block.

这篇关于Eclipse/OSGI,Java 11,JAXB和类加载器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆