JAXB2的 ObjectFactory 类的意义何在?

我刚开始使用 JAXB,并且使用 JAXB2.1.3的 xjc 从我的 XML Schema 生成了一组类。除了为模式中的每个元素生成类之外,它还创建了一个 ObjectFactory 类。

似乎没有什么可以阻止我直接实例化元素,例如。

MyElement element = new MyElement();

而教程似乎更喜欢

MyElement element = new ObjectFactory().createMyElement();

如果我查看 ObjectFactory.java,我会看到:

public MyElement createMyElement() {
return new MyElement();
}

怎么回事?我为什么还要费心保留 ObjectFactory 类呢?我假设如果我从一个改变的模式重新编译,它也会被覆盖。

73875 次浏览

Backwards compatibility, I guess ...

http://weblogs.java.net/blog/kohsuke/archive/2005/08/a_story_of_migr.html:

...No more ObjectFactory.createXYZ. The problem with those factory methods was that they throw a checked JAXBException. Now you can simply do new XYZ(), no more try/catch blocks. (I know, I know, ... this is one of those "what were we thinking!?" things)...

Backward compatibility isn't the only reason. :-P

With more complicated schemas, such as ones that have complicated constraints on the values that an element's contents can take on, sometimes you need to create actual JAXBElement objects. They are not usually trivial to create by hand, so the create* methods do the hard work for you. Example (from the XHTML 1.1 schema):

@XmlElementDecl(namespace = "http://www.w3.org/1999/xhtml", name = "style", scope = XhtmlHeadType.class)
public JAXBElement<XhtmlStyleType> createXhtmlHeadTypeStyle(XhtmlStyleType value) {
return new JAXBElement<XhtmlStyleType>(_XhtmlHeadTypeStyle_QNAME, XhtmlStyleType.class, XhtmlHeadType.class, value);
}

This is how you get a <style> tag into a <head> tag:

ObjectFactory factory = new ObjectFactory();
XhtmlHtmlType html = factory.createXhtmlHtmlType();
XhtmlHeadType head = factory.createXhtmlHeadType();
html.setHead(head);
XhtmlStyleType style = factory.createXhtmlStyleType();
head.getContent().add(factory.createXhtmlHeadTypeStyle(style));

The first three uses of the ObjectFactory could be considered superfluous (though useful for consistency), but the fourth one makes JAXB much, much easier to use. Imaging having to write a new JAXBElement out by hand each time!

As @Chris pointed out, sometimes JAXB cannot work with POJOs, because the schema cannot be mapped exactly on to Java. In these cases, JAXBElement wrapper objects are necessary to provide the additional type information.

There are two concrete examples that I've come across where this is common.

  • If you want to marshal an object of a class that does not have the @XmlRootElement annotation. By default XJC only generates @XmlRootElement for some elements, and not for others. The exact logic for this is a bit complicated, but you can force XJC to generate more @XmlRootElement classes using the "simple binding mode"

  • When your schema uses substituion groups. This is pretty advanced schema usage, but XJC translates substitution groups into Java by making heavy use of JAXBElement wrappers.

So in an XJC-generated object model which makes heavy use of JAXBElement (for whatever reason), you need a way of constructing those JAXBElement instances. The generated ObjectFactory is by far the easiest way to do it. You can construct them yourself, but it's clunky and error-prone to do so.