xmlrootelement jaxbcontext ejemplo java jaxb marshalling

java - jaxbcontext - jaxb spring



JAXB crea contexto y costos de Marshallers (7)

La pregunta es un poco teórica, ¿cuál es el costo de crear JAXB context, marshaller y unmarshaller?

Descubrí que mi código podría beneficiarse al mantener el mismo contexto JAXB y, posiblemente, el mismo marcador de referencias para todas las operaciones de cálculo de referencias en lugar de crear contexto y marcador en cada cálculo.

Entonces, ¿cuál es el costo de crear JAXB context y marshaller / unmarshaller? ¿Está bien crear context + marshaller para cada operación de cálculo de referencias o es mejor evitarlo?


¡¡Aun mejor!! En función de la buena solución de la publicación anterior, cree el contexto una sola vez en el constructor y guárdelo en lugar de la clase.

Reemplazar la línea:

private Class clazz;

Con este:

private JAXBContext jc;

Y el constructor principal con este:

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

entonces en el getMarshaller / getUnmarshaller puedes eliminar esta línea:

JAXBContext jc = JAXBContext.newInstance(clazz);

Esta mejora hace, en mi caso, que los tiempos de procesamiento caigan de 60 ~ 70 ms a solo 5 ~ 10 ms


Es una pena que esto no se describa específicamente en el javadoc. Lo que puedo decir es que Spring usa un JAXBContext global, compartido entre hilos, mientras que crea un nuevo Marshaller para cada operación de clasificación, con un comentario de javadoc en el código que dice que los Marshallers JAXB no son necesariamente seguros para subprocesos.

Lo mismo se dice en esta página: http://jaxb.java.net/guide/Performance_and_thread_safety.html .

Supongo que crear un JAXBContext es una operación costosa, ya que implica escanear clases y paquetes para anotaciones. Pero medirlo es la mejor manera de saberlo.


Idealmente, debería tener un JAXBContext e instancias locales de Marshaller y Unmarshaller .

JAXBContext instancias de JAXBContext son seguras para subprocesos mientras que Marshaller instancias de Marshaller y Unmarshaller no son seguras para subprocesos y nunca deben compartirse entre subprocesos.


JAXB 2.2 ( JSR-222 ) tiene esto que decir, en la sección "4.2 JAXBContext":

Para evitar la sobrecarga implicada en la creación de una instancia de JAXBContext , se recomienda que una aplicación JAXB reutilice una instancia de JAXBContext . Se requiere una implementación de la clase abstracta JAXBContext para que sea segura para subprocesos , por lo tanto, varios subprocesos en una aplicación pueden compartir la misma instancia de JAXBContext.

[..]

La clase JAXBContext está diseñada para ser inmutable y, por lo tanto, segura para el hilo. Dada la cantidad de procesamiento dinámico que potencialmente podría tener lugar al crear una nueva instancia de JAXBContxt, se recomienda que una instancia de JAXBContext se comparta entre subprocesos y se reutilice tanto como sea posible para mejorar el rendimiento de la aplicación.

Desafortunadamente, la especificación no hace ninguna afirmación con respecto a la seguridad de Unmarshaller de Unmarshaller y Marshaller . Por lo tanto, es mejor asumir que no lo son.


Normalmente ThreadLocal problemas como este con un patrón de clase ThreadLocal . Dado el hecho de que necesita un contador de referencias diferente para cada Clase, puede combinarlo con un patrón de mapa único.

Para ahorrarle 15 minutos de trabajo. Aquí sigue mi implementación de una fábrica segura para subprocesos para Jaxb Marshallers y Unmarshallers.

Le permite acceder a las instancias de la siguiente manera ...

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

Y el código que necesitará es una pequeña clase de Jaxb que se ve de la siguiente manera:

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; } }


Nota: Soy el líder de EclipseLink JAXB (MOXy) y miembro del grupo de expertos JAXB 2 ( JSR-222 ).

JAXBContext es seguro para subprocesos y solo debe crearse una vez y reutilizarse para evitar el costo de inicialización de los metadatos varias veces. Marshaller y Unmarshaller no son seguros para subprocesos, pero son livianos para crear y se pueden crear por operación.


Resolví este problema utilizando JAXBContext seguro para subprocesos compartidos y enhebé un / marschallers local (así que teóricamente habrá tantas instancias de un / marshaller como subprocesos que accedieron a ellos) con sincronización solo en la inicialización de 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"); } }