С момента моего последнего поста прошло больше месяца: лето было жарким и солнечным, но я вернулся к работе. Пост этой недели будет посвящен конвертации бобов.
В прошлом я рассказывал о DTO и их неизбирательном использовании в приложениях. Однако я признаю, что DTO иногда необходимы: в этой статье указываются некоторые контексты, в которых они могут быть. Более того, DTO даже может быть недостаточно, что приводит к необходимости просмотра объектов. Так обстоит дело с многоуровневой архитектурой в больших проектах.
Во всех случаях конверсия между объектами каждого типа быстро становится не только рутиной для разработчика, но и гнездом потенциальных ошибок. Чем глупее будет написать код, тем больше вероятность, что разработчик не будет думать об этом, и будет очень трудно найти ошибку, если она есть. Я испытывал это раньше: я не очень хорошо помню время, когда мне приходилось отлаживать код, который я не разработал только для того, чтобы обнаружить, что спустя 2 часа ошибка была вызвана тем, что установщик не имел никакого эффекта. Использование инструмента в таком случае (например, Eclipse или генерация геттеров / сеттеров NetBeans) является хорошей защитой от этих неприятных потенциальных ошибок.
Для преобразования bean в bean есть несколько инструментов:
- Apache Commons Beanutils (исторический)
- Класс Spring BeantUtils и его методы copyProperties () (привязаны к Spring)
- Transmorph
- EZMorph
- и т.п.
Лично я использую 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.