Статьи

Автоматическое преобразование бобов

С момента моего последнего поста прошло больше месяца: лето было жарким и солнечным, но я вернулся к работе. Пост этой недели будет посвящен конвертации бобов.

В прошлом я рассказывал о DTO и их неизбирательном использовании в приложениях. Однако я признаю, что DTO иногда необходимы: в этой статье указываются некоторые контексты, в которых они могут быть. Более того, DTO даже может быть недостаточно, что приводит к необходимости просмотра объектов. Так обстоит дело с многоуровневой архитектурой в больших проектах.

Во всех случаях конверсия между объектами каждого типа быстро становится не только рутиной для разработчика, но и гнездом потенциальных ошибок. Чем глупее будет написать код, тем больше вероятность, что разработчик не будет думать об этом, и будет очень трудно найти ошибку, если она есть. Я испытывал это раньше: я не очень хорошо помню время, когда мне приходилось отлаживать код, который я не разработал только для того, чтобы обнаружить, что спустя 2 часа ошибка была вызвана тем, что установщик не имел никакого эффекта. Использование инструмента в таком случае (например, Eclipse или генерация геттеров / сеттеров NetBeans) является хорошей защитой от этих неприятных потенциальных ошибок.

Для преобразования bean в bean есть несколько инструментов:

Лично я использую Dozer , «Java Bean to Java Bean mapper», который является более мощным, чем свойства простого копирования, и легко настраивается.

Настроить

Примечание: я буду использовать Dozer самым простым способом (без Spring). Использование других конфигураций, конечно, потребует других шагов.

Настройка Dozer требует только нескольких действий:

  • добавить зависимость Maven в Dozer
    <dependency>
      <groupId>net.sf.dozer</groupId>
      <artifactId>dozer</artifactId>
      <version>5.2.2</version>
    </dependency>
  • добавить зависимость времени выполнения XML Beans. Я думаю, что это ошибка проекта, так как это необходимая зависимость для Dozer
    <dependency>
      <groupId>org.apache.xmlbeans</groupId>
      <artifactId>xmlbeans</artifactId>
      <version>2.4.0</version>
      <scope>runtime</scope>
    </dependency>
  • создайте файл XML с именем dozerBeanMapping.xml в корне пути к классам
    <?xml version="1.0" encoding="UTF-8"?>
    <mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
    </mappings>

Готово!

Простейшее отображение

В простейших случаях вам, вероятно, нечего делать, так как ваши различные типы объектов будут иметь одинаковые атрибуты, как name, так и type.

Первое, что нужно сделать, это получить экземпляр самого картографа. Лучше всего сделать его синглтоном: на самом деле, Dozer предлагает синглтон-завод.

После этого Dozer предлагает две простые альтернативы: либо он создает компонент для вас (строка 3 ниже), либо заполняет уже созданный компонент (строка 7).

Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();

PersonTransferObject personTo = mapper.map(personDo, PersonTransferObject.class);

/* PersonTransferObject personTo = new PersonTransferObject();

mapper.map(personDo, personTo) */;

Последнее очень интересно, когда вы используете фреймворки (такие как Struts), которые будут создавать бины и передавать их в методы, которые вам придется переопределить.

Конфигурация сопоставления

До сих пор Dozer не оказался более полезным, чем Commons Beanutils или Spring BeanUtils. Его сила заключается в его конфигурационных способностях. Давайте рассмотрим простой пример: представьте, что вам нужно передать объект Transfer Object в форму Struts (я знаю, что Struts устарел, но я должен использовать его в своей повседневной работе, поэтому, пожалуйста, потерпите меня — этот пример можно использовать в других все равно контексты). В большинстве случаев формы Struts будут иметь строгие атрибуты String, чтобы их можно было отображать на странице HTML и анализировать со страницы HTML. По сути, форма Struts является объектом просмотра.

По сути, проблема под рукой состоит в том, как отобразить бин с типизированными атрибутами на бин с атрибутами String. Отображение числовых значений автоматизировано в Dozer, реальная боль заключается в атрибутах Date. Поскольку Dozer настраивается, просто добавьте следующее в файл сопоставления:

<configuration>
  <date-format>dd/MM/yyyy</date-format>
</configuration>

Вы можете утверждать, что в некоторых случаях вам понадобятся другие форматы: нет проблем, так как это только по умолчанию. Формат даты может быть переопределен на уровне отображения классов или даже на уровне отображения полей!

Расширенная конфигурация

В интернационализированных приложениях предыдущей конфигурации будет недостаточно, поскольку у каждого пользователя должна быть дата, отображаемая в его собственном регионе. Тем не менее, Dozer предвидел этот вариант использования и предоставил вам возможность выбрать отображение.

До сих пор мы полагались на отображение по умолчанию: предоставляем два объекта и, поскольку они имеют одинаковые атрибуты, Dozer выполняет свою работу. Чтобы выбрать между отображениями, вам необходимо явно объявить их в файле сопоставления:

<mapping>
  <class-a>ch.frankel.blog.dozer.domain.PersonTransferObject</class-a>
  <class-b>ch.frankel.blog.dozer.form.PersonForm</class-b>
</mapping>

Эта конфигурация не изменит предыдущий пример, она просто явно отображает сопоставление. Теперь, чтобы иметь контекстное сопоставление, просто добавьте атрибут map-id сопоставления. И так как я объяснил, прежде чем вы сможете изменить анализ / форматирование даты на уровне отображения, давайте сделаем это тоже:

<mapping map-id="en" date-format="MM/dd/yyyy">
  <class-a>ch.frankel.blog.dozer.domain.PersonTransferObject</class-a>
  <class-b>ch.frankel.blog.dozer.form.PersonForm</class-b>
</mapping>
<mapping map-id="fr" date-format="dd/MM/yyyy">
  <class-a>ch.frankel.blog.dozer.domain.PersonTransferObject</class-a>
  <class-b>ch.frankel.blog.dozer.form.PersonForm</class-b>
</mapping>

Чтобы использовать желаемое отображение, просто передайте соответствующий идентификатор в вызове отображения:

mapper.map(personTo, personForm, "en");

За пределами конфигурации

Если ничего не помогает, у вас всегда есть возможность создать свой собственный конвертер. Например, представьте, что вы хотите скрыть первичный ключ базы данных ваших объектов на уровне представления, полагаясь на бизнес-ключ в нем. Поскольку первичный ключ утерян, и все, что у вас есть, это бизнес-ключ, вы должны извлечь PK из уровня базы данных при преобразовании вашего Transfer Object обратно в Entity.

Пользовательские конвертеры могут быть настроены для всего приложения, для конкретного отображения или даже для определенных полей; это означает, что в последнем случае вы можете съесть торт и съесть его тоже. Для случая, представленного выше, просто используйте это в файле конфигурации:

<mapping>
  <class-a>ch.frankel.blog.dozer.domain.PersonTransferObject</class-a>
  <class-b>ch.frankel.blog.dozer.entity.PersonDataObject</class-b>
  <field custom-converter="ch.frankel.blog.dozer.converter.PersonPrimaryKeyConverter">
    <a>businessId</a>
    <b>id</b>
  </field>
</mapping>

Пользовательский конвертер является просто реализацией ch.frankel.blog.dozer.converter.PersonPrimaryKeyConverter, который имеет единственный метод, public Object convert (Object существующиеDestinationFieldValue, Object sourceFieldValue, Class <?> DestinationClass, Class <?> SourceClass).

Дополнительные возможности

Эта небольшая статья еще не рассказала о том, что может сделать Dozer:

  • отображение коллекций
  • отображение перечислений
  • исключение поля
  • изготовленные на заказ бобовые фабрики
  • пользовательские методы получения / установки
  • эталонное сопоставление
  • рамки событий
  • язык выражения
  • и т.п.

Вывод

Что касается размера и использования приложений, которые я разрабатываю, я склонен следовать принципу KISS, и в большинстве случаев я позволяю своим сущностям проходить сквозь слои. В других случаях я поддерживаю использование инструментов автоматического преобразования, чтобы уменьшить вероятность ошибок и усталость разработчика. Dozer — лучший инструмент, который я когда-либо использовал: он достаточно настраиваемый, чтобы его можно было легко использовать в простых случаях использования, и достаточно мощный, чтобы использовать его в сложных случаях. Более того, его документация отличного качества.

Источники для этой статьи доступны здесь в формате Eclipse / Maven.

С http://blog.frankel.ch/automated-beans-conversion