Это первый день моего участия в первом испытании обновлений 2022. Подробную информацию о мероприятии см.:Вызов первого обновления 2022 г.
Сцены
Обычно при разработке серверных проектов из-за иерархической структуры проекта, такой как архитектура MVC и очень популярная в последнее время архитектура DDD, будут соответствующие DO, BO, VO, DTO и т. д. на разных уровнях.Это своего рода класс POJO, и когда мы передаем данные между слоями, нам обычно нужно сопоставлять свойства объекта. Для некоторых простых объектов методы get и set могут использоваться напрямую, или класс инструмента BeanUtils может использоваться для выполнения сопоставления между атрибутами.
Эти коды часто скучны, скучны и могут потребовать многократного преобразования двух объектов друг в друга в разных классах бизнес-обработки. В результате код наполнен большим количеством преобразований get и set.Если используется BeanUtils, проблема может быть обнаружена во время выполнения из-за несогласованных имен полей.
Так есть ли решение этой проблемы?
Ответ заключается в использовании MapStruct, который элегантно решает эти проблемы.
MapStruct — это компонент генератора кода, который следует принципу соглашения, а не конфигурации, что упрощает преобразование между нашими объектами bean-компонентов.
Зачем использовать MapStruct?
Как описано в предыдущей статье, при проектировании многоуровневого приложения необходимо выполнять преобразование между различными объектными моделями, отображением атрибутов, а написание этих кодов вручную не только громоздко, но и чревато ошибками. облегчить эту работу, автоматизировать.
По сравнению с другими платформами сопоставления, такими как BeanUtils или сериализацией и десериализацией Json, MapStruct может генерировать сопоставления во время компиляции, чтобы обеспечить производительность программы и найти ошибки во время компиляции.
Как работает MapStruct?
MapStruct — это, по сути, процессор аннотаций, который можно интегрировать непосредственно в инструменты компиляции, такие как Maven или Gradle.
Взяв Maven в качестве примера, нам нужно сначала добавить зависимости MapStruct к зависимостям, а затем добавитьmapstruct-processorНастраивается в плагине maven.
<properties>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Далее вы можете использовать MapStruct в своем коде.
Сопоставление полей с тем же именем
Например, теперь у нас есть функция для запроса студенческого объекта Student из уровня постоянства, а затем преобразования его в StudentDTO и передачи на бизнес-уровень. Здесь нам нужно преобразовать между объектом Student и объектом StudentDTO.
Student.java
@Data
public class Student{
private int no;
private String name;
}
StudentDTO.java
@Data
public class StudentDTO {
private int no;
private String name;
}
Для преобразования между такими объектами нам нужно создать класс Mapper для сопоставления.
@Mapper(componentModel = "spring")
public interface StudentMapper {
StudentDTO toDto(Student student);
}
@Mapper: это аннотация MapStruct, используемая для создания и генерации реализации карты.
componentModel = "spring": смысл этого атрибута в том, чтобы использовать экземпляр StudentMapper в качестве объекта Spring Bean и поместить его в контейнер Spring, чтобы его можно было легко внедрить в другие бизнес-коды.
Поскольку у Student и StudentDTO одинаковые имена свойств, нам не нужен какой-либо другой код для их явного сопоставления.
Различное сопоставление полей имени
Что делать, если имена полей различаются между DTO и PO?
Student.java
@Data
public class Student{
private int no;
private String name;
private String gender;
}
StudentDTO.java
@Data
public class StudentDTO {
private int no;
private String name;
private String sex;
}
Как показано в приведенном выше коде, в Student и StudentDTO имена полей пола несовместимы. Чтобы реализовать сопоставление в этом случае, просто добавьте следующую аннотацию @Mapping.
@Mapper(componentModel = "spring")
public interface StudentMapper {
@Mapping(source = "gender", target = "sex")
StudentDTO toDto(Student student);
}
Сопоставление свойств пользовательских объектов
Если у каждого студента есть своя адресная информация Address, как с этим быть?
класс исходного объекта
@Data
public class Student{
private int no;
private String name;
private String gender;
private Address address;
}
@Data
public class Address{
private String city;
private String province;
}
целевой класс объектов
@Data
public class StudentDTO{
private int no;
private String name;
private String sex;
private AddressDTO address;
}
@Data
public class AddressDTO{
private String city;
private String province;
}
Чтобы отобразить внутренний объект, нам нужно создать Mapper для внутреннего объекта, а затем импортировать Mapper внутреннего объекта в StudentMapper.
@Mapper(componentModel = "spring")
public interface AddressMapper {
AddressDTO toDto(Address address);
}
@Mapper(componentModel = "spring",uses = {AddressMapper.class})
public interface StudentMapper {
@Mapping(source = "gender", target = "sex")
StudentDTO toDto(Student student);
}
Сопоставление пользовательской логики преобразования
Если преобразование объекта представляет собой не только простое сопоставление между атрибутами, но также должно быть преобразовано в соответствии с некоторой бизнес-логикой, такой как адресная информация Address в каждом студенте, в StudentDTO требуется только город в адресной информации.
класс исходного объекта
@Data
public class Student{
private int no;
private String name;
private String gender;
private Address address;
}
@Data
public class Address{
private String city;
private String province;
}
целевой класс объектов
@Data
public class StudentDTO{
private int no;
private String name;
private String sex;
private String city;
}
В этой ситуации мы можем использовать его непосредственно в исходном коде.address.city, вы также можете использовать пользовательские методы для выполнения логических преобразований.
@Mapper(componentModel = "spring",uses = {AddressMapper.class})
public interface StudentMapper {
@Mapping(source = "gender", target = "sex")
// @Mapping(source = "address.city", target = "city")
@Mapping(source = "address", target = "city",qualifiedByName = "getAddressCity")
StudentDTO toDto(Student student);
@Named("getAddressCity")
default String getChildCircuits(Address address) {
if(address == null) {
return "";
}
return address.getCity();
}
}
карта коллекции
Сопоставление полей коллекции с помощью MapStruct также просто. Например, у каждого студента есть несколько курсов по выбору.List<Course>, для сопоставления в StudentDTOList<CourseDTO>середина.
@Mapper(componentModel = "spring")
public interface CourseMapper {
CourseDTO toDto(Course port);
List<CourseDTO> toCourseDtoList(List<Course> courses);
}
@Mapper(componentModel = "spring",uses = {CourseMapper.class})
public interface StudentMapper {
@Mapping(source = "gender", target = "sex")
@Mapping(source = "address", target = "city",qualifiedByName = "getAddressCity")
CircuitDto toDto(Circuit circuit);
}
Другие сопоставления особых случаев
В дополнение к общему отображению объектов, в некоторых случаях нам может понадобиться установить некоторые фиксированные значения при преобразовании.Предположим, что в StudentDTO есть поле степени, но данные еще не введены, поэтому значение по умолчанию «неизвестно» следует установить здесь. Это можно сделать с помощью аннотации @Mapping.
@Mapping(target = "degree", constant = "未知")
@BeforeMapping и @AfterMapping
@BeforeMappingи@AfterMappingЭто две очень важные аннотации. Вы можете догадаться, глядя на названия. Их можно использовать для некоторой обработки до и после преобразования.
Например, если мы хотим выполнить некоторую проверку данных, инициализацию коллекции и другие функции перед преобразованием, мы можем использовать@BeforeMapping;
Хотите изменить некоторые результаты данных после завершения преобразования, например курсы по выбору, основанные на большем количестве StudentDTOList<CourseDTO>Число, которое нужно указать, если есть выборное полеhaveCourseУстановить логические значения и т. д.
@Mapper(componentModel = "spring",uses = {PortMapper.class})
public interface StudentMapper {
@BeforeMapping
default void setCourses(Student student) {
if(student.getCourses() == null){
student.setCourses(new ArrayList<Course>());
}
}
@Mapping(source = "gender", target = "sex")
StudentDTO toDto(Student student);
@AfterMapping
default void setHaveCourse(StudentDTO studentDto) {
if(studentDto.getCourses()!=null && studentDto.getCourses() >0){
studentDto.setHaveCourse(true);
}
}
}
Суммировать
В этой статье рассказывается, как использовать MapStruct для элегантной реализации сопоставления атрибутов объектов, сокращения кода получения и установки в нашем коде и предотвращения ошибок сопоставления между атрибутами объектов. Как видно из приведенного выше примера кода, MapStruct широко использует аннотации, что позволяет нам легко выполнять сопоставление объектов.
Если вы хотите узнать больше о MapStruct, вы можете продолжить чтение официальной документации.Официальная документация MapStruct
Спасибо за прочтение Если вам это поможет, вы можете поставить Сяо Хей лайк 👍, писать не легко и нужно немного положительного отзыва. 🙏
Я Сяо Хей, программист, который «выслеживает» Интернет.
Текущая вода не соревнуется за первое, а говорить дорого