【问题标题】:Why does JAXBContext need to be told specifically about a class that is already in the package?为什么需要专门告知 JAXBContext 包中已经存在的类?
【发布时间】:2014-12-11 00:30:45
【问题描述】:

这个程序:

import foo.bar.baz.ClassSpecificallyIncluded;  
import javax.xml.bind.JAXBContext;  
public class A {  
    public static void main(String[] args) throws Exception {  
        System.out.println(JAXBContext.newInstance(ClassSpecificallyIncluded.class).toString());  
        System.out.println(JAXBContext.newInstance("foo.bar.baz").toString());  
    }  
}  

为第一个和第二个 JAXBContext 产生不同的输出:

jar:file:/C:/dev/trunk/rt/tomcat/shared/lib/webservices-rt-2.0.1.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class 构建-ID:1.0
此上下文已知的类:
[乙
布尔
字节
字符
foo.bar.baz.Class1
foo.bar.baz.Class1$NestedClass
foo.bar.baz.Class2
foo.bar.baz.ClassSpecificallyIncluded
foo.bar.baz.AnotherClass
com.sun.xml.bind.api.CompositeStructure
双
浮动
整数
java.awt.Image
java.io.File
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Class
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Object
java.lang.Short
java.lang.String
java.lang.Void
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.net.URL
java.util.日历
java.util.Date
java.util.GregorianCalendar
java.util.UUID
javax.activation.DataHandler
javax.xml.bind.JAXBElement
javax.xml.datatype.Duration
javax.xml.datatype.XMLGregorianCalendar
javax.xml.namespace.QName
javax.xml.transform.Source
长
短
无效

jar:file:/C:/dev/trunk/rt/tomcat/shared/lib/webservices-rt-2.0.1.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class 构建-ID:1.0
此上下文已知的类:
[乙
布尔
字节
字符
foo.bar.baz.Class1
foo.bar.baz.Class1$NestedClass
foo.bar.baz.Class2
>>
foo.bar.baz.AnotherClass
com.sun.xml.bind.api.CompositeStructure
双
浮动
整数
java.awt.Image
java.io.File
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Class
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Object
java.lang.Short
java.lang.String
java.lang.Void
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.net.URL
java.util.日历
java.util.Date
java.util.GregorianCalendar
java.util.UUID
javax.activation.DataHandler
javax.xml.bind.JAXBElement
javax.xml.datatype.Duration
javax.xml.datatype.XMLGregorianCalendar
javax.xml.namespace.QName
javax.xml.transform.Source
长
短
无效

然而,ClassSpecificallyIncluded 在 foo.bar.baz 包中:

//  
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6   
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>   
// Any modifications to this file will be lost upon recompilation of the source schema.   
// Generated on: 2012.05.14 at 10:47:17 PM IST   
//  

package foo.bar.baz;  

import javax.xml.bind.annotation.XmlAccessType;  
import javax.xml.bind.annotation.XmlAccessorType;  
import javax.xml.bind.annotation.XmlElement;  
import javax.xml.bind.annotation.XmlRootElement;  


@XmlAccessorType(XmlAccessType.FIELD)  
@XmlType(name = "", propOrder = {  
    "applicationArea",  
    "dataArea"  
})  
@XmlRootElement(name = "ClassSpecificallyIncluded")  
public class ClassSpecificallyIncluded {...  

两个 JAXBContext 的类路径和类加载器是相同的。

那么为什么第二个 JAXBContext 不知道 ClassSpecificallyIncluded 呢?

【问题讨论】:

    标签: jaxb


    【解决方案1】:

    您的问题的答案在这里得到了有效的回答:

    Can you find all classes in a package using reflection?

    答案: 否 按照设计,java 不知道也无法找到包中的每个类。 Java 建立在“及时”的概念之上。这意味着 java 不知道它得到了什么,直到它被要求特定的东西。

    所以从这个角度来看,JAXB 无法找到包中的类。

    如果每次需要上下文时都必须准确地告诉每个类 JAXB,那将很不方便。因此,作为一种方便的方法,JAXB 为您提供了提供包名称的选项。

    为了绕过 java 的限制,它尝试从该包中加载 ObjectFactory 和 jaxb.index 文件。它可以这样做,因为它们是特定的名称。如果找到一个,那么它将它们用作包的清单。如果两者都没有找到,它别无选择,只能中止,因为它不可能知道包里有什么。

    【讨论】:

    • 谢谢。这很有道理。
    【解决方案2】:

    在包名上创建 JAXBContext

    假设我在名为forum20273355 的目录中有以下类:

    • 地址
    • 客户(扩展人)
    • 哺乳动物
    • Person(扩展 Mammal 并具有 Address 类型的属性)

    如果我使用以下代码创建JAXBContext:

    JAXBContext jc = JAXBContext.newInstance("forum20273355");
    

    然后我会得到如下异常:

    Exception in thread "main" javax.xml.bind.JAXBException: Provider com.sun.xml.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index
     - with linked exception:
    [javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index]
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:146)
        at javax.xml.bind.ContextFinder.find(ContextFinder.java:334)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:431)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:394)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:298)
        at forum20273355.Demo.main(Demo.java:8)
    Caused by: javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index
        at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:197)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:172)
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:132)
        ... 5 more
    

    指定 jaxb.index 或 ObjectFactory

    下面我们将创建一个jaxb.index 文件和ObjectFactory,这将导致Person 类被处理以查看发生了什么。

    jaxb.in​​dex

    jaxb.index 是一个文本文件,其中包含一个回车分隔的短类名称列表。

    Person
    

    对象工厂

    import javax.xml.bind.annotation.XmlRegistry;
    
    @XmlRegistry
    public class ObjectFactory {
    
        public Person createPerson() {
            return new Person();
        }
    
    }
    

    已处理的类

    显然Person 已被处理,但超类Mammal 和引用类Address 也是如此。未处理的类是子类Customer。

    forum20273355.Address
    forum20273355.Mammal
    forum20273355.Person
    

    处理子类

    显然我们可以将子类Customer 添加到jaxb.index 或ObjectFactory。我们还可以利用@XmlSeeAlso 注释来实现这一点。

    package forum20273355;
    
    import javax.xml.bind.annotation.XmlSeeAlso;
    
    @XmlSeeAlso({Customer.class})
    public class Person extends Mammal {
    

    现在Customer 也已处理完毕。

    forum20273355.Address
    forum20273355.Customer
    forum20273355.Mammal
    forum20273355.Person
    

    更新

    您在评论中写的所有内容都是真实的。缺少的是jaxb.index 文件或ObjectFactory 类是启动进程所必需的。从 XML 模式生成模型时,会生成一个 ObjectFactory,其中包含引导模型所需的所有引用。当您从 Java 类开始时,我建议从 Java 类而不是包名进行引导,但同样的规则适用。

    但是 Sun Oracle 文档说:JAXBContext.newInstance( "com.acme.foo:com.acme.bar" ) JAXBContext 实例被初始化 来自冒号分隔的 Java 包名列表。

    没错,包名指定了元数据的查找位置。

    每个 java 包都包含 JAXB 映射类、模式派生类 和/或用户注释的类。

    是的。

    另外,java 包可能包含 JAXB 包注解 必须处理。 (参见 JLS 第 3 版,第 7.4.1 节。包 注释)。

    没错,包注解肯定会被应用。

    【讨论】:

    • 但是 Sun Oracle 文档说: JAXBContext.newInstance( "com.acme.foo:com.acme.bar" ) JAXBContext 实例是从冒号分隔的 Java 包名称列表中初始化的。每个 java 包都包含 JAXB 映射类、模式派生类和/或用户注释类。此外,java 包可能包含必须处理的 JAXB 包注释。 (参见 JLS 第 3 版,第 7.4.1 节。包注释)。它在哪里说明了子类等?
    • @user1461450 - 您的评论是真实的,并且与我的回答不冲突。我已添加更新以提供更多信息。
    • 嗯。此包中的 ObjectFactory 没有 ClassSpecificallyIncluded 的 QName。 (这不是我的包,只是想使用它。)以前没有注意到。但是 ClassSpecificallyIncluded 不是子类或超类。它自己。因此,要求添加 XmlSeeAlso 是不自然的。无论如何,似乎包没有正确生成,因此出现了问题。感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 2016-09-04
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多