54

它就在那里,在它应该被索引的包中。不过,当我打电话时

JAXBContext jc = JAXBContext.newInstance("my.package.name");

我得到一个 JAXBException 说

“my.package.name”不包含 ObjectFactory.class 或 jaxb.in​​dex

虽然它确实包含两者。

什么有效,但不是我想要的,是

JAXBContext jc = JAXBContext.newInstance(my.package.name.SomeClass.class);

来自其他人的这个问题出现在相当多的邮件列表和论坛上,但似乎没有得到答案。

我在 OpenJDK 6 上运行它,所以我得到了源包并将我的调试器放到了库中。它首先查找 jaxb.properties,然后查找系统属性,但均未找到,它尝试使用 com.sun.internal.xml.bind.v2.ContextFactory 创建默认上下文。在那里,异常被抛出(内部ContextFactor.createContext(String ClassLoader, Map)),但我看不到发生了什么,因为源不在这里。

预计到达时间:

从 ContentFactory 的源代码来看,我在这里找到了,这可能是一段无法按预期工作的代码:

/**
 * Look for jaxb.index file in the specified package and load it's contents
 *
 * @param pkg package name to search in
 * @param classLoader ClassLoader to search in
 * @return a List of Class objects to load, null if there weren't any
 * @throws IOException if there is an error reading the index file
 * @throws JAXBException if there are any errors in the index file
 */
private static List<Class> loadIndexedClasses(String pkg, ClassLoader classLoader) throws IOException, JAXBException {
    final String resource = pkg.replace('.', '/') + "/jaxb.index";
    final InputStream resourceAsStream = classLoader.getResourceAsStream(resource);

    if (resourceAsStream == null) {
        return null;
    }

根据我以前的 经验,我猜测这与运行它的 OSGi 容器的类加载机制有关。不幸的是,我在这里仍然有点不够深入。

4

10 回答 10

63

好的,这需要相当多的挖掘,但答案并不令人惊讶,甚至没有那么复杂:

JAXB 找不到 jaxb.in​​dex,因为默认情况下,newInstance(String)使用当前线程的类加载器(由 返回Thread.getContextClassLoader())。这在 Felix 中不起作用,因为 OSGi 包和框架的线程具有单独的类加载器。

解决方案是从某个地方获取合适的类加载器并使用newInstance(String, ClassLoader). 我从包含的包中的一个类中获得了一个合适的类加载器,jaxb.index出于灵活性原因,一个明智的选择可能是ObjectFactory

ClassLoader cl = my.package.name.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("my.package.name", cl);

也许您也可以获取Bundle实例正在使用的类加载器,但我无法弄清楚如何,并且上述解决方案对我来说似乎是安全的。

于 2009-06-25T13:08:30.437 回答
6

我正在从事的项目中遇到了类似的问题。阅读http://jaxb.java.net/faq/index.html#classloader后,我意识到 JAXBContext 无法找到包含 jaxb.in​​dex 的包。

我会尽量说明这一点。

我们有

Bundle A
   -- com.a
      A.java
        aMethod()
        {
            B.bMethod("com.c.C");
        }
MANIFEST.MF
Import-Package: com.b, com.c         

Bundle B
   -- com.b
      B.java
        bmethod(String className)
        {
            Class clazz = Class.forName(className);
        }

Export-Package: com.b

Bundle C
   -- com.c
      C.java
        c()
        {
            System.out.println("hello i am C");
        }

Export-Package: com.c

JAXB相关。B 类是 JAXBContext,bMethod 是 newInstance()

如果您熟悉 OSGi 包限制,那么现在必须非常清楚Bundle B没有导入包com.c类 C类 B可见,因此它无法实例化 C。

解决方案是将ClassLoader传递给 bMethod。这个 ClassLoader 应该来自一个正在导入 com.c 的包。在这种情况下,我们可以传递A.class.getClassLoader()因为包 A 正在导入 com.c

希望这会有所帮助。

于 2011-09-30T17:41:38.610 回答
4

对于同样的问题,我通过手动将包放入导入来解决它。

于 2013-01-21T04:22:45.753 回答
1

如果您在项目中使用 maven,则只需使用此库:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-osgi</artifactId>
    <version>2.2.7</version>
</dependency>

它是为 Glasfish 服务器创建的,但也适用于 Tomcat(选中)。使用这个库,您可以轻松地将 JAXB 与 OSGI 捆绑包一起使用。

于 2013-09-05T09:15:40.310 回答
0

编辑2:

我曾经在我的应用程序中遇到过类似的奇怪的类加载问题。如果我将它作为普通应用程序运行,一切正常,但是当我将它作为 Windows 服务调用时,它开始因 ClassNotFoundExceptions 而失败。分析表明,线程的类加载器不知何故为空。我通过在线程上设置 SystemClassLoader 解决了这个问题:

// ...
thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
thread.start();
// ...

不知道您的容器是否允许这种更改。

于 2009-06-25T10:35:15.507 回答
0

我刚刚遇到这个问题。对我来说,解决方案是使用 IBM 的 JRE 而不是 Oracle 的。似乎 JAXB 实现在那个方面对 OSGI 更友好。

于 2012-03-04T02:10:46.183 回答
0

我通过将包含我生成的类的包添加ObjectFactory到我的包定义的<Private-Package>一部分成功地解决了这个问题,加上org.jvnet.jaxb2_commons.*

于 2014-01-10T11:02:33.927 回答
0

可能还有另一种情况会导致此问题。

当您安装并启动一个包时,该包导出包含 jaxb.in​​dex 或 objectFactory.java 的包

然后请确保导入类的包已停止或指向正确的包名。

还要检查 pom.xml 中的导出和导入语句

在 servicemix(karaf) osgi 容器中面临类似问题

于 2014-02-28T06:09:07.347 回答
0

对我来说,问题是与我开发的模块无关的单元测试在 pom.xml 中对我的模块没有依赖关系。由于从共享配置文件中获取包列表,UT 仍然识别出我的模块。

运行 UT 时,它没有编译新模块,因此它没有生成 ObjectFactory.java,因此即使在编译模块时我也收到了错误,我能够看到 ObjectFactory.java

添加了以下依赖项:

<dependency>
    <groupId>com.myCompany</groupId>
    <artifactId>my-module-name</artifactId>
    <version>${project.version}</version>
    <scope>test</scope>
</dependency>
于 2016-09-15T08:56:27.713 回答
-1

我的解决方案是:

JAXBContext 上下文 = JAXBContext.newInstance( new Class[]{"my.package.name"} );

或者

JAXBContext 上下文 = JAXBContext.newInstance( new Class[]{class.getName()} );

或者

一个完整的解决方案:

public static <T> T deserializeFile(Class<T> _class, String _xml) {

        try {

            JAXBContext context = JAXBContext.newInstance(new Class[]{_class});
            Unmarshaller um = context.createUnmarshaller();

            File file = new File(_xml);
            Object obj = um.unmarshal(file);

            return _class.cast(obj);

        } catch (JAXBException exc) {
            return null;
        }
    }

工作 100%

于 2014-01-30T20:24:42.603 回答