JavaRush /Java блогу /Random-KY /Mapstruct деген эмне жана аны SpringBoot тиркемелеринде т...
Roman Beekeeper
Деңгээл

Mapstruct деген эмне жана аны SpringBoot тиркемелеринде тестирлөө үчүн кантип туура конфигурациялоо керек

Группада жарыяланган

Фон

Баарыңарга салам, кымбаттуу досторум жана окурмандарым! Макаланы жазуудан мурун, бир аз маалымат... Мен жакында эле Mapstruct китепканасы менен иштөөдө көйгөйгө туш болдум, бул жерде мен телеграм каналымда кыскача сүрөттөп бердим . Комментарийлерде посттун көйгөйү чечилди, буга мурунку долбоордогу кесиптешим жардам берди. Mapstruct деген эмне жана аны SpringBoot тиркемелеринде тестирлөө үчүн кантип туура конфигурациялоо керек.  1-1-бөлүкОшондон кийин мен бул тема боюнча макала жазууну чечтим, бирок, албетте, биз тар көз карашты карманбайбыз жана алгач тездик менен иштөөгө, Mapstruct деген эмне экенин жана ал эмне үчүн керек экенин түшүнүүгө аракет кылабыз жана чыныгы мисалды колдонуп, мурда пайда болгон кырдаалды жана аны чечүүнүн жолдорун талдоо. Ошондуктан, мен бардыгын иш жүзүндө көрүү үчүн макаланы окуу менен катар бардык эсептөөлөрдү жасоону сунуш кылам. Баштоодон мурун, менин телеграм каналыма жазылыңыз , мен ал жерде өзүмдүн иш-аракеттеримди чогултам, Java жана жалпысынан IT тармагын өнүктүрүү жөнүндө ойлорду жазам. Жазылгансызбы? Абдан жакшы! Мейли, эми кетели!

Mapstruct, FQ?

Ыкчам типтеги коопсуз төө буурчак карталары үчүн code генератору. Биздин биринчи милдет Mapstruct деген эмне экенин жана ал эмне үчүн керек экенин аныктоо. Жалпысынан алганда, бул тууралуу расмий сайттан окуй аласыз. Сайттын башкы бетинде суроолорго үч жооп бар: бул эмне? Эмне үчүн? Кантип? Муну да жасоого аракет кылабыз:

Бул эмне?

Mapstruct – бул интерфейстер аркылуу сүрөттөлгөн конфигурациялардын негизинде түзүлгөн codeду колдонуу менен кээ бир an objectилердин an objectтерин башка an objectтердин an objectилерине картага түшүрүүгө (карта, жалпысынан алганда, алар дайыма ушундай дешет: карта, карта ж.б.) жардам берген китепкана.

Эмне үчүн?

Көпчүлүк учурда, биз көп катмарлуу тиркемелерди (маалымат базасы менен иштөө үчүн катмар, бизнес логика катмары, тиркеменин тышкы дүйнө менен өз ара аракеттенүү катмары) иштеп чыгабыз жана ар бир катмардын маалыматтарды сактоо жана иштетүү үчүн өзүнүн an objectтери бар. . Жана бул маалыматтар бир an objectтен экинчисине өтүү аркылуу катмардан катмарга өтүшү керек. Бул ыкма менен иштебегендер үчүн бул бир аз татаал сезorши мүмкүн. Мисалы, бизде Студенттик маалымат базасы бар. Бул an objectтин маалыматтары бизнес-логикалык (кызматтар) катмарына өткөндө, биз Студент классынан StudentModel классына маалыматтарды өткөрүп беришибиз керек. Андан кийин, бизнес логикасы менен бардык манипуляциялардан кийин, маалыматтарды сыртка чыгаруу керек. Бул үчүн бизде StudentDto классы бар. Албетте, биз StudentModel классынан StudentDtoга маалыматтарды өткөрүп беришибиз керек. Которула турган ыкмаларды ар бир жолу кол менен жазуу эмгекти кеп талап кылат. Мындан тышкары, бул code базасында сакталышы керек болгон кошумча code. Сиз ката кетириши мүмкүн. Жана Mapstruct мындай ыкмаларды компиляция стадиясында жаратат жана аларды түзүлгөн булактарда сактайт.

Кантип?

Аннотацияларды колдонуу. Биз жөн гана китепканага бул интерфейстеги ыкмаларды бир an objectтен экинчи an objectке которуу үчүн колдонсо болорун билдирген негизги Mapper annotationсы бар annotationны түзүшүбүз керек. Студенттер жөнүндө мурда айткандай, биздин учурда бул StudentMapper интерфейси болот, анда маалыматтарды бир катмардан экинчисине өткөрүүнүн бир нече ыкмалары бар:
public class Student {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentDTO {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentModel {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}
Бул класстар үчүн карта түзүүчүнү түзөбүз (мындан ары интерфейс деп атайбыз, анда биз эмнени жана кайда өткөргүбүз келгенин сүрөттөйт):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
Бул ыкманын кооздугу, эгерде талаалардын аталыштары жана түрлөрү ар кандай класстарда бирдей болсо (биздин учурубуздагыдай), анда Mapstruct орнотуулары компиляция стадиясында StudentMapper интерфейсинин негизинде керектүү ишке ашырууну түзүү үчүн жетиштүү. которуп берет. Демек, бул ачык-айкын болуп калды, туурабы? Келгиле, андан ары барып, Spring Boot тиркемесинде ишти талдоо үчүн чыныгы мисалды колдонолу.

Spring Boot жана Mapstruct ишинин мисалы

Бизге керек болгон биринчи нерсе - Spring Boot долбоорун түзүү жана ага Mapstruct кошуу. Бул үчүн, менде GitHub-да репозиторийлердин шаблондору бар уюм бар жана Spring Boot башталышы - алардын бири. Анын негизинде биз жаңы долбоор түзөбүз: Андан кийин долбоордуMapstruct деген эмне жана аны SpringBoot тиркемелеринде тестирлөө үчүн кантип туура конфигурациялоо керек.  1-2-бөлүк алабыз . Ооба, достор, эгер долбоорду пайдалуу деп тапсаңыз, ага жылдыз бериңиз , ошондуктан мен муну бекер жасап жаткан жокмун. Бул долбоордо биз жумушта кабыл алган жана менин Telegram каналымдагы постто сүрөттөгөн жагдайды ачып беребиз . Мен билбегендер үчүн кырдаалды кыскача баяндап берем: картачылар үчүн тесттерди жазганда (башкача айтканда, биз мурда сүйлөшкөн интерфейсти ишке ашыруу үчүн), биз тесттердин мүмкүн болушунча тезирээк өтүшүн каалайбыз. Картачылардын эң жөнөкөй варианты тестти жүргүзүүдө SpringBootTest annotationсын колдонуу болуп саналат, ал Spring Boot тиркемесинин бардык ApplicationContext'ин тандап алат жана тесттин ичиндеги тестке керектүү карта салуучуну киргизет. Бирок бул вариант ресурсту көп талап кылат жана көп убакытты талап кылат, ошондуктан ал бизге ылайыктуу эмес. Биз жөн гана керектүү картаны түзүүчү жана анын ыкмалары биз күткөндөй иштешин текшерген бирдик тестин жазышыбыз керек. Эмне үчүн сизге тезирээк иштөө үчүн тесттер керек? Эгерде тесттер көпкө созулса, анда ал бүт иштеп чыгуу процессин жайлатат. Сыноолор жаңы codeдон өтмөйүнчө, бул code туура деп эсептелбейт жана тестирлөөгө алынbyte, бул өндүрүшкө алынbyte жана иштеп чыгуучу ишти аягына чыгара элек дегенди билдирет. Иши күмөнсүз китепканага тест жазуунун эмне кереги бар? Бирок биз тест жазуубуз керек, анткени биз картачыны канчалык туура сүрөттөгөнүбүздү жана ал биз күткөн нерсени аткарып жатабы же жокпу, текшерип жатабыз. Биринчиден, ишибизди жеңилдетүү үчүн, келгиле, pom.xml файлына дагы бир көз карандылыкты кошуп, Ломбокту долбоорубузга кошолу:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Биздин долбоордо биз моделдик класстардан (бизнес логикасы менен иштөө үчүн колдонулат) тышкы дүйнө менен байланышуу үчүн колдонгон DTO класстарына өтүшүбүз керек болот. Биздин жөнөкөйлөтүлгөн versionда биз талаалар өзгөрбөйт жана биздин картачылар жөнөкөй болот деп ойлойбуз. Бирок, каалоо бар болсо, Mapstruct менен кантип иштөө керек, аны кантип конфигурациялоо керек жана анын артыкчылыктарынан кантип пайдалануу керектиги жөнүндө кененирээк макала жазса болот. Бирок, анда, анткени бул макала бир топ узун болот. Бизде бир студент бар дейли, ал катышкан лекциялардын жана лекторлордун тизмеси. Үлгү пакетин түзөлү . Мунун негизинде биз жөнөкөй моделди түзөбүз:
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
анын лекциялары
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
жана лекторлор
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Жана модель пакетинин жанында dto пакетин түзүңүз :
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
лекциялар
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
жана лекторлор
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Эми лекция моделдеринин жыйнагын DTO лекцияларынын жыйнагына которо турган мапперди түзөлү. Биринчи нерсе - долбоорго Mapstruct кошуу. Бул үчүн, биз алардын расмий сайтын колдонобуз , баары ошол жерде сүрөттөлгөн. Башкача айтканда, биз эс тутумубузга бир көз карандылыкты жана плагинди кошушубуз керек (эгерде эс тутум деген эмне жөнүндө суроолоруңуз болсо, анда 1-берене жана 2-берене ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
жана <Build/> блогундагы эс тутумда. бизде али боло элек:
<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.5.1</version>
           <configuration>
               <source>1.8</source>
               <target>1.8</target>
               <annotationProcessorPaths>
                   <path>
                       <groupId>org.mapstruct</groupId>
                       <artifactId>mapstruct-processor</artifactId>
                       <version>1.4.2.Final</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
       </plugin>
   </plugins>
</build>
Андан кийин, dto жана моделдин жанында карталоочу пакетти түзөлү . Биз буга чейин көрсөткөн класстардын негизинде дагы беш картачы түзүшүңүз керек болот:
  • Mapper LectureModel <-> LectureDTO
  • Mapper List<LectureModel> <-> List<LectureDTO>
  • Mapper LecturerModel <-> LecturerDTO
  • Картачынын тизмеси<ЛекторМодель> <-> Тизме<ЛекторДТО>
  • Mapper StudentModel <-> StudentDTO
Барыңыз:

LectureMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LecturerDTO dto);
}

LectureListMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LectureMapper.class)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}

LecturerMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LectureDTO dto);
}

LecturerListMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LecturerModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LecturerMapper.class)
public interface LecturerListMapper {
   List<LecturerModel> toModelList(List<LecturerDTO> dloList);
   List<LecturerDTO> toDTOList(List<LecturerModel> modelList);
}

StudentMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.StudentDTO;
import com.github.romankh3.templaterepository.springboot.model.StudentModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
public interface StudentMapper {
   StudentDTO toDTO(StudentModel model);
   StudentModel toModel(StudentDTO dto);
}
Мапперлерде биз башка картачыларга кайрыла турганыбызды өзүнчө белгилей кетүү керек. Бул StudentMapperде жасалгандай, Картачынын annotationсындагы uses талаасы аркылуу жасалат :
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Бул жерде биз лекциялардын тизмесин жана лекторлордун тизмесин туура картага түшүрүү үчүн эки картаны колдонобуз. Эми биз codeубузду түзүп, ал жерде эмне жана кантип бар экенин көрүшүбүз керек. Бул mvn clean compile буйругун колдонуу менен жасалышы мүмкүн . Бирок, белгилүү болгондой, биздин картачылардын Mapstruct ишке ашырууларын түзүп жатканда, картанын ишке ашыруулары талаалардын үстүнөн жазган эмес. Неге? Ломбоктон Data annotationсын алуу мүмкүн эмес экени белгилүү болду. Анан бир нерсе кылуу керек эле... Ошондуктан макалада жаңы бөлүм ачылды.

Lombok жана Mapstruct байланыштыруу

Бир нече мүнөттүк издөөдөн кийин биз Lombok менен Mapstructти белгилүү бир жол менен туташтырышыбыз керек экен. Бул тууралуу Mapstruct documentациясында маалымат бар . Mapstruct иштеп чыгуучулары тарабынан сунушталган мисалды карап чыккандан кийин, келгиле, pom.xml жаңырталы: Келгиле, өзүнчө versionларды кошолу:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Жетишпеген көз карандылыкты кошолу:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
Келгиле, компилятор плагинибизди жаңырталы, ал Lombok менен Mapstructти туташтыра алат:
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.5.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>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>${lombok.version}</version>
           </path>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok-mapstruct-binding</artifactId>
               <version>${lombok-mapstruct-binding.version}</version>
           </path>
       </annotationProcessorPaths>
   </configuration>
</plugin>
Ушундан кийин баары иштеши керек. Долбоорубузду дагы бир ирет түзөлү. Бирок Mapstruct түзгөн класстарды кайдан тапса болот? Алар түзүлгөн булактарда: ${projectDir}/target/generated-sources/annotations/ Mapstruct деген эмне жана аны SpringBoot тиркемелеринде тестирлөө үчүн кантип туура конфигурациялоо керек.  1-3-бөлүк Mapstruct постунан менин көңүлүм калганын түшүнүүгө даярбыз, келгиле, картачылар үчүн тесттерди түзүүгө аракет кылалы.

Биз картачыларыбызга тест жазабыз

Мен интегралдык тест түзүп жаткан учурда картачылардын бирин сынай турган тез жана жөнөкөй тест түзөм жана анын бүтүү убактысы жөнүндө кабатыр болбо:

LectureMapperTest

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class LectureMapperTest {

   @Autowired
   private LectureMapper mapperUnderTest;

   @Test
   void shouldProperlyMapModelToDto() {
       //given
       LectureModel model = new LectureModel();
       model.setId(11L);
       model.setName("lecture name");

       //when
       LectureDTO dto = mapperUnderTest.toDTO(model);

       //then
       Assertions.assertNotNull(dto);
       Assertions.assertEquals(model.getId(), dto.getId());
       Assertions.assertEquals(model.getName(), dto.getName());
   }

   @Test
   void shouldProperlyMapDtoToModel() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(11L);
       dto.setName("lecture name");

       //when
       LectureModel model = mapperUnderTest.toModel(dto);

       //then
       Assertions.assertNotNull(model);
       Assertions.assertEquals(dto.getId(), model.getId());
       Assertions.assertEquals(dto.getName(), model.getName());
   }
}
Бул жерде, SpringBootTest annotationсын колдонуп, биз толугу менен applicationContextди ишке киргизебиз жана андан Autowired annotationсын колдонуп, тестирлөө үчүн керектүү классты чыгарып алабыз. Тестирлөөнүн ылдамдыгы жана оңойлугу жагынан бул абдан жакшы. Сыноо ийгorктүү өттү, баары жакшы. Бирок биз башка жол менен барабыз жана картачы үчүн бирдик тестин жазабыз, мисалы, LectureListMapper...
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl();

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Mapstruct жараткан ишке ашыруулар биздин долбоор менен бир класста болгондуктан, биз аларды тесттерибизде оңой колдоно алабыз. Баары сонун көрүнөт - annotationлар жок, биз эң жөнөкөй жол менен керектүү классты түзөбүз. Бирок биз тестти иштеткенде, анын бузулуп каларын жана консолдо NullPointerException болорун түшүнөбүз... Себеби, LectureListMapper картасын ишке ашыруу төмөнкүдөй көрүнөт:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T21:46:12+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   @Autowired
   private LectureMapper lectureMapper;

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
Эгерде биз NPE (NullPointerException үчүн кыска) карасак, биз аны lectureMapper өзгөрмөсүнөн алабыз , ал инициализацияланбаган болуп чыгат. Бирок биздин ишке ашырууда бизде өзгөрмөлөрдү инициализациялоочу конструктор жок. Mapstruct картачыны ушундай жол менен ишке ашырганынын себеби дал ушул! Жазында төө буурчакты класстарга бир нече жол менен кошо аласыз, сиз аларды талаа аркылуу, жогоруда айтылгандай, Autowired annotationсы менен сайсаңыз болот же конструктор аркылуу сайсаңыз болот. Тестти аткаруу убактысын оптималдаштыруу керек болгондо, мен жумушта ушундай көйгөйлүү кырдаалга туш болдум. Эч нерсе кыла алbyte деп ойлоп, телеграм каналыма сыздадым. Анан алар мага комментарийлерде жардам берип, инъекциянын стратегиясын ыңгайлаштырууга болот дешти. Mapper интерфейсинде injectionStrategy талаасы бар , ал жөн гана InjectionStrategy аталышын кабыл алат , анын эки мааниси бар: FIELD жана CONSTRUCTOR . Эми, муну бorп туруп, бул жөндөөнү картачыларыбызга кошолу; Мен аны LectureListMapper аркылуу мисал катары көрсөтөм :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Кошумча бөлүгүн кара шрифт менен белгилеп койдум. Келгиле, бул параметрди башкалардын бардыгына кошолу жана картачылар жаңы линия менен түзүлүшү үчүн долбоорду кайра компиляция кылалы. Бул аткарылгандан кийин, келгиле, LectureListMapper үчүн картаны ишке ашыруу кандайча өзгөргөнүн карап көрөлү (бизге керектүү бөлүгү калың менен белгиленген):
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T22:25:37+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   private final LectureMapper lectureMapper;

   @Autowired
   public LectureListMapperImpl(LectureMapper lectureMapper) {

       this.lectureMapper = lectureMapper;
   }

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
Азыр Mapstruct конструктор аркылуу карта инъекциясын ишке ашырды. Дал ушул нерсеге жетишүүгө аракет кылганбыз. Эми биздин тест компиляцияны токтотот, аны жаңырталы жана алалы:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl(new LectureMapperImpl());

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Эми биз тестти өткөрсөк, баары күтүлгөндөй иштейт, анткени LectureListMapperImplде биз ага керектүү LectureMapper тапшырабыз... Жеңиш! Бул силер үчүн кыйын эмес, бирок мен ыраазымын: Достор, баары адаттагыдай эле, менин GitHub аккаунтума , Telegram аккаунтума жазылыңыз . Ал жерде мен өзүмдүн ишмердүүлүгүмдүн жыйынтыгын жарыялайм, чынында эле пайдалуу нерселер бар) Мен сиздерди өзгөчө телеграм каналынын талкуу тобуна кошулууга чакырам . Кимдир бирөөнүн техникалык суроосу болсо, ошол жерден жооп ала тургандай болуп калат. Бул формат баарына кызык, ким эмнени билет экенин окуп, тажрыйба топтосо болот.

Корутунду

Бул макаланын алкагында биз Mapstruct сыяктуу керектүү жана көп колдонулган продукт менен тааныштык. Биз бул эмне экенин, эмне үчүн жана кантип түшүндүк. Чыныгы мисалды колдонуп, биз эмне кылса болорун жана аны кантип өзгөртүүгө болорун сездик. Биз ошондой эле конструктор аркылуу төө буурчак инъекциясын кантип орнотууну карап чыктык, андыктан картачыларды туура сынап көрүүгө болот. Mapstruct'тин кесиптештери өз продуктунун колдонуучуларына карта салгычтарды кантип инъекциялоону так тандоого мүмкүндүк берди, бул үчүн биз аларга ыраазычылык билдиребиз. БИРОК, Spring төө буурчакты конструктор аркылуу инъекциялоону сунуш кылганына карабастан, Mapstructтин балдары демейки боюнча талаа аркылуу инъекцияны орнотушкан. Эмнеге андай? Жооп жок. Биз билбеген себептер болушу мүмкүн деп ойлойм, ошондуктан алар ушундай кылышты. Жана алардан билүү үчүн, мен алардын расмий продукт репозиторийинде GitHub маселесин түздүм.
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION