JAXB创建上下文和marshallers成本

时间:2011-09-13 10:33:31

标签: java jaxb marshalling

问题有点理论化,创建JAXB上下文,marshaller和unmarshaller的成本是多少?

我发现我的代码可以从保持相同的JAXB上下文和可能与所有编组操作相同的编组器中受益,而不是在每次编组时创建上下文和编组器。

那么创建JAXB上下文和marshaller / unmarshaller的成本是多少?是否可以为每个编组操作创建context + marshaller,或者最好避免它?

8 个答案:

答案 0 :(得分:213)

注意:我是EclipseLink JAXB (MOXy)主管,是JAXB 2(JSR-222)专家组的成员。

JAXBContext是线程安全的,只应创建一次并重复使用,以避免多次初始化元数据的成本。 MarshallerUnmarshaller不是线程安全的,但是可以轻量级创建,并且可以按操作创建。

答案 1 :(得分:35)

理想情况下,您应该拥有单身JAXBContext以及MarshallerUnmarshaller的本地实例。

JAXBContext个实例是线程安全的,而MarshallerUnmarshaller个实例是线程安全的,不应该跨线程共享。

答案 2 :(得分:13)

遗憾的是,这在javadoc中没有具体描述。我能说的是Spring使用了一个在线程之间共享的全局JAXBContext,而它为每个编组操作创建了一个新的编组器,代码中有javadoc comment表示JAXB marshallers不一定是线程安全的。

此页面上也是如此:https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety

我猜想创建一个JAXBContext是一项代价高昂的操作,因为它涉及扫描类和注释的包。但衡量它是了解最佳方式。

答案 3 :(得分:3)

我使用共享线程安全 JAXBContext 解决了这个问题,并解决了本地 un / marschallers 的问题 (理论上,会有多个 un / marshaller 实例,因为有访问它们的线程) 仅在 un / marshaller 的初始化时同步。

private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
    protected synchronized Unmarshaller initialValue() {
        try {
            return jaxbContext.createUnmarshaller();
        } catch (JAXBException e) {
            throw new IllegalStateException("Unable to create unmarshaller");
        }
    }
};
private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
    protected synchronized Marshaller initialValue() {
        try {
            return jaxbContext.createMarshaller();
        } catch (JAXBException e) {
            throw new IllegalStateException("Unable to create marshaller");
        }
    }
};

private final JAXBContext jaxbContext;

private MyClassConstructor(){
    try {
        jaxbContext = JAXBContext.newInstance(Entity.class);
    } catch (JAXBException e) {
        throw new IllegalStateException("Unable to initialize");
    }
}

答案 4 :(得分:3)

JAXB 2.2(JSR-222)在“4.2 JAXBContext”一节中有这样的说法:

  

为了避免创建JAXBContext实例所涉及的开销,a   鼓励重用JAXB应用程序以重用JAXBContext实例。一个   需要实现抽象类JAXBContext   线程安全,因此,应用程序中的多个线程可以共享   相同的JAXBContext实例。

     

[..]

     

JAXBContext类被设计为不可变的,因此线程安全。   考虑到可能采取的动态处理量   在创建JAXBContxt的新实例时,建议使用它   一个JAXBContext实例在线程之间共享并重用为   尽可能提高应用程序性能。

不幸的是,该规范没有对UnmarshallerMarshaller的线程安全做出任何声明。所以最好假设它们不是。

答案 5 :(得分:1)

更好!!根据上面帖子中的好解决方案,在构造函数中创建上下文 just-once ,并保存它而不是类。

替换行:

  private Class clazz;

这一个:

  private JAXBContext jc;

这个主要构造函数:

  private Jaxb(Class clazz)
  {
     this.jc = JAXBContext.newInstance(clazz);
  }

所以在getMarshaller / getUnmarshaller中你可以删除这一行:

  JAXBContext jc = JAXBContext.newInstance(clazz);

在我的情况下,这种改进使处理时间从60~70ms下降到5~10ms

答案 6 :(得分:1)

在枚举中创建JAXBContext并在应用程序线程中访问它以创建Marshaller / Unmarshaller就足够了。

public enum MyApplicationJAXBContext {

    REQ(Request.class), RESP(Response.class);

    private final JAXBContext jaxbContext;

    MyApplicationJAXBContext(Class contextClass) {

        try {
            jaxbContext = JAXBContext.newInstance(contextClass);
        } catch (JAXBException e) 
            throw new RunTimeException(e);
           // Lets caller decide what to do ?
        }
    }

    public JAXBContext getJaxbContext() {
        return jaxbContext;
    }
}


public class MyAppCallable implements Callable<Response> {

private final Request req;
public MyAppCallable(Request req) {
    this.req = req;
}


public Response call() {

Marshaller marshaller = MyApplicationJAXBContext.REQ.getJaxbContext().createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Anything else you want to configure properties
Unmarshaller unmarshaller = MyApplicationJAXBContext.RESP.getJaxbContext().createUnmarshaller();

/** 
All other logic you want to do after req/rsp usage and return Response
**/

}

}

答案 7 :(得分:0)

我通常使用ThreadLocal类模式来解决这样的问题。 鉴于您需要为每个类使用不同的编组器,您可以将其与singleton - 地图模式结合使用。

为你节省15分钟的工作时间。以下是我对Jaxb Marshallers和Unmarshallers的线程安全工厂的实现。

它允许您按如下方式访问实例...

Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();

您需要的代码是一个小的Jaxb类,如下所示:

public class Jaxb
{
  // singleton pattern: one instance per class.
  private static Map<Class,Jaxb> singletonMap = new HashMap<>();
  private Class clazz;

  // thread-local pattern: one marshaller/unmarshaller instance per thread
  private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
  private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();

  // The static singleton getter needs to be thread-safe too, 
  // so this method is marked as synchronized.
  public static synchronized Jaxb get(Class clazz)
  {
    Jaxb jaxb =  singletonMap.get(clazz);
    if (jaxb == null)
    {
      jaxb = new Jaxb(clazz);
      singletonMap.put(clazz, jaxb);
    }
    return jaxb;
  }

  // the constructor needs to be private, 
  // because all instances need to be created with the get method.
  private Jaxb(Class clazz)
  {
     this.clazz = clazz;
  }

  /**
   * Gets/Creates a marshaller (thread-safe)
   * @throws JAXBException
   */
  public Marshaller getMarshaller() throws JAXBException
  {
    Marshaller m = marshallerThreadLocal.get();
    if (m == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      m = jc.createMarshaller();
      marshallerThreadLocal.set(m);
    }
    return m;
  }

  /**
   * Gets/Creates an unmarshaller (thread-safe)
   * @throws JAXBException
   */
  public Unmarshaller getUnmarshaller() throws JAXBException
  {
    Unmarshaller um = unmarshallerThreadLocal.get();
    if (um == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      um = jc.createUnmarshaller();
      unmarshallerThreadLocal.set(um);
    }
    return um;
  }
}