JAXB创build上下文和marshallers成本

这个问题有点理论上,创buildJAXB上下文,编组和解组器的成本是多less?

我发现我的代码可以受益于保持相同的JAXB上下文和可能相同的编组,而不是在每个编组上创build上下文和编组。

那么创buildJAXB上下文和编组/解组器的成本是多less? 可以为每个编组操作创build上下文+编组,还是最好避免它?

注意:我是EclipseLink JAXB(MOXy)领导和JAXB 2( JSR-222 )专家组的成员。

JAXBContext是线程安全的,应该只创build一次并重用,以避免多次初始化元数据的开销。 MarshallerUnmarshaller不是线程安全的,但轻量级创build,可以创build每个操作。

理想情况下,你应该有一个单身JAXBContextMarshallerUnmarshaller本地实例。

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

可惜的是,这在javadoc中没有具体描述。 我可以告诉的是,Spring使用全局JAXBContext,在线程之间共享,而它为每个编组操作创build一个新的编组器,在代码中有一个javadoc注释 ,说JAXB marshallers不一定是线程安全的。

同样在这个页面上说: http : //jaxb.java.net/guide/Performance_and_thread_safety.html 。

我猜想创build一个JAXBContext是一个代价高昂的操作,因为它涉及扫描类和包注释。 但是测量它是知道的最好方法。

我解决了这个问题,使用共享线程安全的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"); } } 

更好! 基于以上文章的良好解决scheme,在构造函数中创build一次上下文,并保存它而不是类。

更换线路:

  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

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

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

[..]

JAXBContext类被devise为不可变的,因此是线程安全的。 鉴于在创buildJAXBContxt的新实例时可能发生的dynamic处理量,build议跨线程共享JAXBContext实例,并尽可能重用以提高应用程序性能。

不幸的是,这个规范并没有对UnmarshallerMarshaller线程安全提出任何要求。 所以最好假设他们不是。

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

为了节省你15分钟的工作。 下面是Jaxb Marshalers和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; } }