JAXB создает контекст и затраты маршаллеров

Вопрос немного теоретический, какова стоимость создания контекста JAXB, маршаллера и unmarshaller?

Я обнаружил, что мой код может выиграть от сохранения одного и того же контекста JAXB и, возможно, одного и того же маршаллера для всех операций маршалинга, а не для создания контекста и маршаллера при каждом маршалинге.

Итак, какова стоимость создания контекста JAXB и marshaller / unmarshaller? Можно ли создать контекст + маршаллер для каждой операции маршалинга или лучше избегать его?

Примечание. Я – лидер EclipseLink JAXB (MOXy) и член экспертной группы JAXB 2 ( JSR-222 ).

JAXBContext является streamобезопасным, его следует создавать только один раз и повторно использовать, чтобы избежать затрат на инициализацию метаданных несколько раз. Marshaller и Unmarshaller не являются streamобезопасными, но Unmarshaller для создания и могут быть созданы за операцию.

В идеале вы должны иметь singleton JAXBContext и локальные экземпляры Marshaller и Unmarshaller .

JAXBContext являются streamобезопасными, в то время Unmarshaller экземпляры Marshaller и Unmarshaller не являются streamобезопасными и не должны делиться нитями.

Жаль, что это не описано конкретно в javadoc. Я могу сказать, что Spring использует глобальный JAXBContext, разделяемый между streamами, тогда как он создает новый маршаллер для каждой операции маршаллинга с комментарием javadoc в коде, в котором говорится, что маршаллы JAXB не обязательно являются streamобезопасными.

То же самое сказано на этой странице: http://jaxb.java.net/guide/Performance_and_thread_safety.html .

Я предполагаю, что создание JAXBContext является дорогостоящей операцией, поскольку оно включает в себя сканирование classов и пакетов для аннотаций. Но измерение – лучший способ узнать.

Я решил эту проблему, используя общие streamи JAXBContext и thread local un / marschallers (так теоретически, будет столько же экземпляров un / marshaller, сколько есть streamов, к которым они обращаются) с синхронизацией только при инициализации un / marshaller .

 private final ThreadLocal unmarshallerThreadLocal = new ThreadLocal() { protected synchronized Unmarshaller initialValue() { try { return jaxbContext.createUnmarshaller(); } catch (JAXBException e) { throw new IllegalStateException("Unable to create unmarshaller"); } } }; private final ThreadLocal marshallerThreadLocal = new ThreadLocal() { 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"); } } 

JAXB 2.2 ( JSR-222 ) имеет это в разделе «4.2 JAXBContext»:

Чтобы избежать накладных расходов на создание экземпляра JAXBContext , JAXB-приложение рекомендуется повторно использовать экземпляр JAXBContext . Реализация абстрактного classа JAXBContext должна быть streamобезопасной , поэтому несколько streamов в приложении могут совместно использовать один и тот же экземпляр JAXBContext.

[..]

Класс JAXBContext предназначен для неизменяемости и, следовательно, для streamобезопасности. Учитывая объем динамической обработки, который потенциально может иметь место при создании нового экземпляра JAXBContxt, рекомендуется, чтобы экземпляр JAXBContext был разделен по streamам и использовался как можно больше для повышения производительности приложения.

К сожалению, в спецификации нет никаких претензий в отношении безопасности Unmarshaller и Marshaller . Поэтому лучше предположить, что это не так.

Даже лучше!! Основываясь на хорошем решении из поста выше, создайте контекст только один раз в конструкторе и сохраните его вместо classа.

Замените линию:

  private Class clazz; 

с этим:

  private JAXBContext jc; 

И главный конструктор с этим:

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

поэтому в getMarshaller / getUnmarshaller вы можете удалить эту строку:

  JAXBContext jc = JAXBContext.newInstance(clazz); 

Это улучшение делает в моем случае, что время обработки падает с 60 до 70 мс до 5 ~ 10 мс

Обычно я решаю такие проблемы с помощью шаблона classа ThreadLocal . Учитывая тот факт, что для каждого classа вам нужен другой маршаллер, вы можете комбинировать его с шаблоном singleton -map.

Чтобы сэкономить вам 15 минут, работы. Здесь следует моя реализация поточно-надежного Factory для Jashb Marshallers и Unmarshallers.

Он позволяет вам получить доступ к экземплярам следующим образом …

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

И код, который вам понадобится, – это небольшой class Jaxb, который выглядит следующим образом:

 public class Jaxb { // singleton pattern: one instance per class. private static Map singletonMap = new HashMap<>(); private Class clazz; // thread-local pattern: one marshaller/unmarshaller instance per thread private ThreadLocal marshallerThreadLocal = new ThreadLocal<>(); private ThreadLocal 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; } } 
  • Маршал C ++ struct array в C #
  • Могу ли я заставить JAXB не преобразовывать «в», например, при сортировке XML?
  • Давайте будем гением компьютера.