Избавьтесь от BeanUtils, MapStruct - YYDS

задняя часть
Избавьтесь от BeanUtils, MapStruct - YYDS

Это первый день моего участия в первом испытании обновлений 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

Спасибо за прочтение Если вам это поможет, вы можете поставить Сяо Хей лайк 👍, писать не легко и нужно немного положительного отзыва. 🙏

Я Сяо Хей, программист, который «выслеживает» Интернет.

Текущая вода не соревнуется за первое, а говорить дорого