В предыдущей статье "Почему Alibaba запрещает использование Apache Beanutils для копирования атрибутов?", я сравнил несколько классов инструментов для копирования атрибутов.
Затем некоторые читатели в области комментариев сообщили, что MapStruct — это настоящий аромат, поэтому я нашел время, чтобы узнать о MapStruct. В итоге я обнаружил, что это действительно сказочный аромат жареной курочки.
В этой статье мы кратко представим использование MapStruct, а затем сравним его с несколькими другими классами инструментов.
Зачем вам MapStruct?
Прежде всего, поговорим о том, для каких сценариев подходят такие фреймворки, как MapStruct, и почему на рынке так много подобных фреймворков.
В проектировании архитектуры программного обеспечения многоуровневая структура является наиболее распространенной и наиболее важной. Многие люди знакомы с трехуровневой архитектурой, четырехуровневой архитектурой и т. д.
Некоторые даже говорили:«Любая проблема в информатике может быть решена путем добавления непрямого среднего уровня или, если это не сработает, двух».
Однако по мере того, как архитектура программного обеспечения становится все более и более многослойной, модели данных между каждым слоем будут сталкиваться с проблемой взаимного преобразования.Как правило, мы можем видеть в коде различные O, такие как DO, DTO, VO Wait.
Как правило, для одной и той же модели данных мы используем разные модели данных на разных уровнях.Например, на уровне хранения данных мы используем DO для абстрагирования бизнес-сущности; на уровне бизнес-логики мы используем DTO для представления объекта передачи данных; на уровне представления мы инкапсулируем объект в VO для взаимодействия с фронтом. конец.
Затем прозрачная передача данных от внешнего интерфейса к уровню сохраняемости данных (передача от уровня сохраняемости к внешнему интерфейсу) требует взаимного преобразования между объектами, то есть сопоставления между разными объектными моделями.
Обычно мы можем использовать get/set и другие методы для выполнения операций сопоставления полей одну за другой, например:
personDTO.setName(personDO.getName());
personDTO.setAge(personDO.getAge());
personDTO.setSex(personDO.getSex());
personDTO.setBirthday(personDO.getBirthday());
Однако написание такого кода отображения является утомительной и подверженной ошибкам задачей. Цель таких фреймворков, как MapStruct, — максимально упростить эту работу, автоматизировав ее.
Использование MapStruct
Структура карты(mapstruct.org/) — это генератор кода, который значительно упрощает реализацию сопоставлений между типами Java-бинов на основе подхода «соглашение вместо конфигурации». Сгенерированный код сопоставления использует чистые вызовы методов, поэтому он быстрый, типобезопасный и простой для понимания.
Соглашение о конфигурации, также известное как программирование по соглашению, представляет собой парадигму разработки программного обеспечения, которая направлена на сокращение количества решений, которые должен принять разработчик программного обеспечения, и получить преимущества простоты без ущерба для гибкости.
Предположим, у нас есть два класса, которые нужно преобразовать друг в друга, PersonDO и PersonDTO соответственно.Определения классов следующие:
public class PersonDO {
private Integer id;
private String name;
private int age;
private Date birthday;
private String gender;
}
public class PersonDTO {
private String userName;
private Integer age;
private Date birthday;
private Gender gender;
}
Мы демонстрируем, как использовать MapStruct для сопоставления компонентов.
Чтобы использовать MapStruct, вам сначала нужно зависеть от связанных с ним пакетов jar.Метод зависимости maven выглядит следующим образом:
...
<properties>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Поскольку MapStruct должен генерировать код преобразования в компиляторе, необходимо настроить ссылку на mapstruct-processor в подключаемом модуле maven-compiler-plugin. Эта часть будет представлена позже.
После этого нам нужно определить интерфейс для отображения, основной код выглядит следующим образом:
@Mapper
interface PersonConverter {
PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
@Mappings(@Mapping(source = "name", target = "userName"))
PersonDTO do2dto(PersonDO person);
}
использовать аннотации@Mapper
Определите интерфейс Converter и определите в нем метод do2dto. Тип входного параметра метода — PersonDO, а тип выходного параметра — PersonDTO. Этот метод используется для преобразования PersonDO в PersonDTO.
Код теста выглядит следующим образом:
public static void main(String[] args) {
PersonDO personDO = new PersonDO();
personDO.setName("Hollis");
personDO.setAge(26);
personDO.setBirthday(new Date());
personDO.setId(1);
personDO.setGender(Gender.MALE.name());
PersonDTO personDTO = PersonConverter.INSTANCE.do2dto(personDO);
System.out.println(personDTO);
}
Выходной результат:
PersonDTO{userName='Hollis', age=26, birthday=Sat Aug 08 19:00:44 CST 2020, gender=MALE}
Как видите, мы используем MapStruct для идеального преобразования PersonDO в PersonDTO.
Как видно из приведенного выше кода, использование MapStruct относительно простое, в основном полагающееся на@Mapper
аннотация.
Но мы знаем, что в большинстве случаев имена атрибутов, типы и т. д. между двумя классами, которые нам нужно преобразовать друг в друга, не совсем совпадают, и в некоторых случаях мы не хотим выполнять сопоставление напрямую, так как разобраться с этим?
Фактически, MapStruct также хорошо справляется с этой задачей.
MapStruct обрабатывает сопоставление полей
Прежде всего, мы можем четко сказать вам, что если типы и имена атрибутов исходного объекта в двух преобразуемых классах согласуются с атрибутами целевого объекта, соответствующие атрибуты будут автоматически сопоставлены.
Итак, как действовать в особых обстоятельствах?
Как сопоставить несовместимые имена
Как и в приведенном выше примере, name используется для представления имени пользователя в PersonDO, а userName используется для представления имени пользователя в PersonDTO, поэтому как сопоставлять параметры.
затем используйте@Mapping
Аннотированный, вам нужно только использовать аннотацию в сигнатуре метода и указать имя исходного объекта и имя целевого объекта для преобразования.Например, чтобы сопоставить значение имени с именем пользователя, вы можете использовать следующее методы:
@Mapping(source = "name", target = "userName")
Типы, которые могут быть автоматически сопоставлены
Помимо несоответствия имени, есть еще частный случай, т. е. тип несовместим, например, в приведенном выше примере пол пользователя представлен типом String в PersonDO, а в PersonDTO используется перечисление Genter. для представления пола пользователя.
В настоящее время типы несовместимы, и необходимо решить проблему взаимного преобразования.
На самом деле MapStruct автоматически сопоставит некоторые типы, и нам не нужно делать дополнительную настройку, например, в примере мы автоматически преобразуем тип String в тип перечисления.
Как правило, автоматическое преобразование типов может выполняться в следующих ситуациях:
- Примитивные типы и соответствующие им типы-оболочки.
- Между типом оболочки примитивного типа и типом String
- Между типом String и типом перечисления
пользовательская константа
Если мы хотим определить фиксированное значение для некоторых атрибутов в процессе отображения преобразования, мы можем использовать константу в это время.
@Mapping(source = "name", constant = "hollis")
Как сопоставить несовместимые типы
В приведенном выше примере, если нам нужно добавить свойство домашнего адреса к объекту Person, мы обычно определяем класс HomeAddress в PersonoDTO для представления домашнего адреса, а в классе Person мы обычно используем тип String для представления адреса. домашний адрес. .
Это требует использования JSON для взаимного преобразования между HomeAddress и String. В этом случае MapStruct также может поддерживать.
public class PersonDO {
private String name;
private String address;
}
public class PersonDTO {
private String userName;
private HomeAddress address;
}
@Mapper
interface PersonConverter {
PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
@Mapping(source = "userName", target = "name")
@Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")
PersonDO dto2do(PersonDTO dto2do);
default String homeAddressToString(HomeAddress address){
return JSON.toJSONString(address);
}
}
Нам нужно только определить метод в PersonConverter (поскольку PersonConverter — это интерфейс, поэтому метод по умолчанию может быть определен в версиях после JDK 1.8), функция этого метода — преобразовать HomeAddress в тип String.
метод по умолчанию: новая языковая функция, представленная в Java 8, помеченная ключевым словом по умолчанию, метод, помеченный по умолчанию, должен предоставлять реализацию, и подклассы могут выбирать, реализовывать или не реализовывать метод.
Затем в методе dto2do преобразование типов может быть достигнуто с помощью следующих аннотаций:
@Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")
Выше приведено преобразование пользовательского типа, и некоторые преобразования типов поддерживаются самим MapStruct, например преобразование между String и Date:
@Mapping(target = "birthday",dateFormat = "yyyy-MM-dd HH:mm:ss")
Вышеприведенное кратко представляет некоторые часто используемые методы сопоставления полей, которые также представляют собой несколько сценариев, с которыми я часто сталкиваюсь в своей работе.Чтобы узнать больше ситуаций, вы можете проверить официальные примеры (GitHub.com/карта структура/но…
Производительность MapStruct
Сказав так много об использовании MapStruct, можно увидеть, что использование MapStruct относительно просто, а функция сопоставления полей очень мощная, так как же ее производительность?
Ссылаться на"Почему Alibaba запрещает использование Apache Beanutils для копирования атрибутов?", мы проводим тестирование производительности на MapStruct.
Затраты времени на выполнение 1000, 10000, 100000 и 1000000 отображений соответственно составляют: 0 мс, 1 мс, 3 мс, 6 мс.
можно увидеть,MapStruct требует очень мало времени по сравнению с несколькими другими инструментами..
Итак, почему MapStruct может работать так хорошо?
Фактически, самая большая разница между MapStruct и другими типами фреймворков заключается в следующем:По сравнению с другими платформами сопоставления, MapStruct создает сопоставления bean-компонентов во время компиляции, что обеспечивает высокую производительность, позволяет заблаговременно сообщать о проблемах и позволяет разработчикам проводить тщательную проверку ошибок.
Помните, когда мы представили зависимости MapStruct, мы добавили поддержку mapstruct-processor в maven-compiler-plugin?
И мы используем в коде множество аннотаций, предоставляемых MapStruct, что позволяет MapStruct напрямую генерировать код сопоставления компонентов во время компиляции, что эквивалентно написанию множества сеттеров и геттеров вместо нас.
Например, мы определяем в коде следующий Mapper:
@Mapper
interface PersonConverter {
PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
@Mapping(source = "userName", target = "name")
@Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")
@Mapping(target = "birthday",dateFormat = "yyyy-MM-dd HH:mm:ss")
PersonDO dto2do(PersonDTO dto2do);
default String homeAddressToString(HomeAddress address){
return JSON.toJSONString(address);
}
}
После компиляции кода автоматически генерируется PersonConverterImpl:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-08-09T12:58:41+0800",
comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
)
class PersonConverterImpl implements PersonConverter {
@Override
public PersonDO dto2do(PersonDTO dto2do) {
if ( dto2do == null ) {
return null;
}
PersonDO personDO = new PersonDO();
personDO.setName( dto2do.getUserName() );
if ( dto2do.getAge() != null ) {
personDO.setAge( dto2do.getAge() );
}
if ( dto2do.getGender() != null ) {
personDO.setGender( dto2do.getGender().name() );
}
personDO.setAddress( homeAddressToString(dto2do.getAddress()) );
return personDO;
}
}
Во время выполнения при отображении bean-компонентов метод dto2do класса PersonConverterImpl будет вызываться напрямую, так что делать особо нечего, просто установить и получить в памяти.
Таким образом, поскольку многое делается во время компиляции, MapStruct будет хорошо работать во время выполнения и имеет дополнительное преимущество, заключающееся в выявлении проблем во время компиляции.
Так что если в коде будет проблема с отображением полей, то приложение не скомпилируется, вынуждая разработчика решать эту проблему.
Суммировать
В этой статье представлен класс инструмента сопоставления полей в Java, MapStruct, его использование относительно просто, а его функции очень полны, что позволяет справиться с сопоставлением полей в различных ситуациях.
А поскольку он генерирует реальный код отображения во время компиляции, производительность во время выполнения значительно повышается.
Очень рекомендую, очень ароматный! ! !