JavaRush /Blog Java /Random-MS /Apakah itu Mapstruct dan cara mengkonfigurasinya dengan b...

Apakah itu Mapstruct dan cara mengkonfigurasinya dengan betul untuk ujian unit dalam aplikasi SpringBoot

Diterbitkan dalam kumpulan

Latar belakang

Halo semua, rakan dan pembaca yang dikasihi! Sebelum kami menulis artikel, sedikit latar belakang... Saya baru-baru ini menghadapi masalah bekerja dengan perpustakaan Mapstruct , yang saya terangkan secara ringkas dalam saluran telegram saya di sini . Masalah dengan siaran telah diselesaikan dalam ulasan; rakan sekerja saya dari projek sebelumnya membantu dengan ini. Apakah itu Mapstruct dan cara mengkonfigurasinya dengan betul untuk ujian unit dalam aplikasi SpringBoot.  Bahagian 1 - 1Selepas itu, saya memutuskan untuk menulis artikel mengenai topik ini, tetapi sudah tentu kita tidak akan mengambil pandangan yang sempit dan mula-mula akan cuba untuk mempercepatkan, memahami apa itu Mapstruct dan mengapa ia diperlukan, dan menggunakan contoh sebenar kita akan menganalisis situasi yang timbul sebelum ini dan cara menyelesaikannya. Oleh itu, saya amat mengesyorkan melakukan semua pengiraan selari dengan membaca artikel untuk mengalami segala-galanya dalam amalan. Sebelum kita mula, langgan saluran telegram saya , saya mengumpulkan aktiviti saya di sana, menulis pemikiran tentang pembangunan di Jawa dan IT secara umum. Melanggan? Hebat! Nah, sekarang mari kita pergi!

Mapstruct, soalan?

Penjana kod untuk pemetaan kacang jenis selamat yang cepat. Tugas pertama kami adalah untuk mengetahui apa itu Mapstruct dan mengapa kami memerlukannya. Secara umum, anda boleh membaca tentangnya di laman web rasmi. Pada halaman utama tapak terdapat tiga jawapan kepada soalan: apakah itu? Untuk apa? Bagaimana? Kami akan cuba melakukan ini juga:

Apa ini?

Mapstruct ialah perpustakaan yang membantu memetakan (peta, secara amnya, itulah yang selalu mereka katakan: peta, peta, dll.) objek sesetengah entiti ke dalam objek entiti lain menggunakan kod yang dijana berdasarkan konfigurasi yang diterangkan melalui antara muka.

Untuk apa?

Untuk sebahagian besar, kami membangunkan aplikasi berbilang lapisan (lapisan untuk bekerja dengan pangkalan data, lapisan logik perniagaan, lapisan untuk interaksi aplikasi dengan dunia luar) dan setiap lapisan mempunyai objek sendiri untuk menyimpan dan memproses data . Dan data ini perlu dipindahkan dari lapisan ke lapisan dengan memindahkan dari satu entiti ke entiti yang lain. Bagi mereka yang belum pernah menggunakan pendekatan ini, ini mungkin kelihatan agak rumit. Sebagai contoh, kami mempunyai entiti untuk pangkalan data Pelajar. Apabila data entiti ini pergi ke lapisan logik perniagaan (perkhidmatan), kita perlu memindahkan data daripada kelas Pelajar ke kelas StudentModel. Seterusnya, selepas semua manipulasi dengan logik perniagaan, data perlu dikeluarkan di luar. Dan untuk ini kami mempunyai kelas StudentDto. Sudah tentu, kita perlu menghantar data dari kelas StudentModel kepada StudentDto. Menulis dengan tangan setiap kali kaedah yang akan dipindahkan adalah intensif buruh. Selain itu, ini adalah kod tambahan dalam pangkalan kod yang perlu dikekalkan. Anda boleh membuat kesilapan. Dan Mapstruct menjana kaedah sedemikian pada peringkat penyusunan dan menyimpannya dalam sumber terjana.

Bagaimana?

Menggunakan anotasi. Kami hanya perlu mencipta anotasi yang mempunyai anotasi Mapper utama yang memberitahu perpustakaan bahawa kaedah dalam antara muka ini boleh digunakan untuk menterjemah dari satu objek ke objek yang lain. Seperti yang saya katakan sebelum ini tentang pelajar, dalam kes kami ini akan menjadi antara muka StudentMapper, yang akan mempunyai beberapa kaedah untuk memindahkan data dari satu lapisan ke lapisan yang lain:
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;
}
Untuk kelas ini kami mencipta pemeta (selepas ini kita akan panggil antara muka, yang menerangkan perkara yang ingin kita pindahkan dan di mana):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
Keindahan pendekatan ini ialah jika nama dan jenis medan adalah sama dalam kelas yang berbeza (seperti dalam kes kami), maka tetapan untuk Mapstruct sudah cukup untuk menjana pelaksanaan yang diperlukan berdasarkan antara muka StudentMapper pada peringkat penyusunan, yang akan menterjemah. Jadi ia sudah menjadi lebih jelas, bukan? Mari pergi lebih jauh dan gunakan contoh sebenar untuk menganalisis kerja dalam aplikasi Spring Boot.

Contoh Spring Boot dan Mapstruct berfungsi

Perkara pertama yang kita perlukan ialah mencipta projek Spring Boot dan menambah Mapstruct kepadanya. Untuk perkara ini, saya mempunyai organisasi dalam GitHub dengan templat untuk repositori dan permulaan untuk Spring Boot adalah salah satu daripadanya. Berdasarkannya, kami mencipta projek baharu: Apakah itu Mapstruct dan cara mengkonfigurasinya dengan betul untuk ujian unit dalam aplikasi SpringBoot.  Bahagian 1 - 2Seterusnya, kami mendapat projek . Ya, kawan-kawan, berikan projek itu bintang jika anda mendapati ia berguna, jadi saya akan tahu bahawa saya tidak melakukan ini dengan sia-sia. Dalam projek ini, kami akan mendedahkan situasi yang saya terima di tempat kerja dan diterangkan dalam siaran di saluran Telegram saya . Saya akan menggariskan secara ringkas situasi bagi mereka yang tidak tahu: apabila kami menulis ujian untuk pemeta (iaitu, untuk pelaksanaan antara muka yang kami bincangkan sebelum ini), kami mahu ujian itu lulus secepat mungkin. Pilihan paling mudah dengan pemeta ialah menggunakan anotasi SpringBootTest semasa menjalankan ujian, yang akan mengambil keseluruhan ApplicationContext aplikasi Spring Boot dan menyuntik pemeta yang diperlukan untuk ujian di dalam ujian. Tetapi pilihan ini intensif sumber dan memerlukan lebih banyak masa, jadi ia tidak sesuai untuk kami. Kita perlu menulis ujian unit yang hanya mencipta pemeta yang dikehendaki dan menyemak bahawa kaedahnya berfungsi tepat seperti yang kita harapkan. Mengapa anda memerlukan ujian untuk berjalan lebih pantas? Jika ujian mengambil masa yang lama, ia melambatkan keseluruhan proses pembangunan. Sehingga ujian lulus pada kod baharu, kod ini tidak boleh dianggap betul dan tidak akan diambil untuk ujian, yang bermaksud ia tidak akan diambil dalam pengeluaran dan yang bermaksud bahawa pembangun belum menyelesaikan kerja. Nampaknya, mengapa menulis ujian untuk perpustakaan yang operasinya tidak diragukan lagi? Namun kami perlu menulis ujian, kerana kami sedang menguji sejauh mana kami menerangkan pemeta dengan betul dan sama ada ia melakukan apa yang kami harapkan. Pertama sekali, untuk memudahkan kerja kita, mari tambahkan Lombok pada projek kita dengan menambahkan satu lagi kebergantungan pada pom.xml:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Dalam projek kami, kami perlu memindahkan daripada kelas model (yang digunakan untuk bekerja dengan logik perniagaan) ke kelas DTO, yang kami gunakan untuk berkomunikasi dengan dunia luar. Dalam versi ringkas kami, kami akan menganggap bahawa medan tidak berubah dan pemeta kami akan menjadi mudah. Tetapi, jika ada keinginan, adalah mungkin untuk menulis artikel yang lebih terperinci tentang cara bekerja dengan Mapstruct, cara mengkonfigurasinya, dan cara memanfaatkan faedahnya. Tetapi kemudian, kerana artikel ini akan menjadi agak panjang. Katakan kita mempunyai seorang pelajar dengan senarai kuliah dan pensyarah yang dia hadiri. Mari buat pakej model . Berdasarkan ini, kami akan membuat model mudah:
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;
}
syarahan beliau
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
dan pensyarah
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Dan buat pakej dto di sebelah pakej model :
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;
}
ceramah
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
dan pensyarah
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Sekarang mari kita cipta pemeta yang akan menterjemah koleksi model kuliah kepada koleksi kuliah DTO. Perkara pertama yang perlu dilakukan ialah menambah Mapstruct pada projek. Untuk melakukan ini, kami akan menggunakan laman web rasmi mereka , semuanya diterangkan di sana. Iaitu, kita perlu menambah satu pergantungan dan pemalam pada ingatan kita (jika anda mempunyai soalan tentang apa itu memori, di sini anda pergi, Artikel1 dan Artikel2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
dan dalam memori dalam blok <build/>. yang belum kita miliki:
<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>
Seterusnya, mari buat pakej pemeta di sebelah dto dan model . Berdasarkan kelas yang kami tunjukkan sebelum ini, anda perlu mencipta lima lagi pemeta:
  • Mapper LectureModel <-> LectureDTO
  • Senarai Mapper<LectureModel> <-> Senarai<LectureDTO>
  • Mapper LecturerModel <-> LecturerDTO
  • Senarai Mapper<LecturerModel> <-> List<LecturerDTO>
  • Mapper StudentModel <-> StudentDTO
Pergi:

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);
}

PensyarahMapper

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);
}
Perlu diingatkan secara berasingan bahawa dalam pemeta kita merujuk kepada pemeta lain. Ini dilakukan melalui medan kegunaan dalam anotasi Mapper, seperti yang dilakukan dalam StudentMapper:
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Di sini kami menggunakan dua pemeta untuk memetakan senarai kuliah dan senarai pensyarah dengan betul. Sekarang kita perlu menyusun kod kita dan melihat apa yang ada dan bagaimana. Ini boleh dilakukan menggunakan perintah mvn clean compile . Tetapi, ternyata, apabila mencipta pelaksanaan Mapstruct pemeta kami, pelaksanaan pemeta tidak menimpa medan. kenapa? Ternyata tidak mungkin untuk mengambil anotasi Data dari Lombok. Dan sesuatu perlu dilakukan... Oleh itu, kami mempunyai bahagian baharu dalam artikel itu.

Menghubungkan Lombok dan Mapstruct

Selepas beberapa minit mencari, ternyata kami perlu menyambung Lombok dan Mapstruct dengan cara tertentu. Terdapat maklumat tentang ini dalam dokumentasi Mapstruct . Selepas meneliti contoh yang dicadangkan oleh pembangun dari Mapstruct, mari kemas kini pom.xml kami: Mari tambah versi berasingan:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Mari tambahkan pergantungan yang hilang:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
Dan mari kemas kini pemalam pengkompil kami supaya ia boleh menyambungkan Lombok dan 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>
Selepas ini semuanya harus berjalan lancar. Mari kita susun semula projek kita. Tetapi di manakah anda boleh mencari kelas yang dihasilkan oleh Mapstruct? Mereka berada dalam sumber terjana: ${projectDir}/target/generated-sources/annotations/ Apakah itu Mapstruct dan cara mengkonfigurasinya dengan betul untuk ujian unit dalam aplikasi SpringBoot.  Bahagian 1 - 3 Sekarang kita bersedia untuk menyedari kekecewaan saya daripada siaran Mapstruct, mari cuba buat ujian untuk pemeta.

Kami menulis ujian untuk pemeta kami

Saya akan membuat ujian cepat dan mudah yang akan menguji salah satu pemeta dalam kes di mana kami mencipta ujian penyepaduan dan jangan risau tentang masa penyiapannya:

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());
   }
}
Di sini, menggunakan anotasi SpringBootTest, kami melancarkan keseluruhan applicationContext dan daripadanya, menggunakan anotasi Autowired, kami mengekstrak kelas yang kami perlukan untuk ujian. Dari sudut kelajuan dan kemudahan menulis ujian, ini sangat bagus. Ujian lulus dengan jayanya, semuanya baik-baik saja. Tetapi kita akan pergi ke arah lain dan menulis ujian unit untuk pemeta, contohnya, 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());
   }
}
Memandangkan pelaksanaan yang dijana oleh Mapstruct berada dalam kelas yang sama dengan projek kami, kami boleh menggunakannya dengan mudah dalam ujian kami. Semuanya kelihatan hebat - tiada anotasi, kami mencipta kelas yang kami perlukan dengan cara yang paling mudah dan itu sahaja. Tetapi apabila kami menjalankan ujian, kami akan memahami bahawa ia akan ranap dan akan ada NullPointerException dalam konsol... Ini kerana pelaksanaan pemeta LectureListMapper kelihatan seperti:
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;
   }
}
Jika kita melihat NPE (singkatan untuk NullPointerException), kita mendapatkannya daripada lectureMapper pembolehubah , yang ternyata tidak dimulakan. Tetapi dalam pelaksanaan kami, kami tidak mempunyai pembina yang mana kami boleh memulakan pembolehubah. Inilah sebab mengapa Mapstruct melaksanakan pemeta dengan cara ini! Pada Musim Bunga, anda boleh menambah kacang ke kelas dalam beberapa cara, anda boleh menyuntiknya melalui medan bersama-sama dengan anotasi Autowired, seperti yang dilakukan di atas, atau anda boleh menyuntiknya melalui pembina. Saya mendapati diri saya berada dalam situasi bermasalah di tempat kerja apabila saya perlu mengoptimumkan masa pelaksanaan ujian. Saya fikir tiada apa yang boleh dilakukan mengenainya dan mencurahkan kesakitan saya di saluran Telegram saya. Dan kemudian mereka membantu saya dalam komen dan mengatakan bahawa mungkin untuk menyesuaikan strategi suntikan. Antara muka Mapper mempunyai medan injectionStrategy , yang hanya menerima nama InjectionStrategy , yang mempunyai dua nilai: FIELD dan CONSTRUCTOR . Sekarang, mengetahui perkara ini, mari tambahkan tetapan ini pada pemeta kami; Saya akan menunjukkannya menggunakan LectureListMapper sebagai contoh :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Saya menyerlahkan dengan tebal bahagian yang saya tambah. Mari tambahkan pilihan ini untuk semua yang lain dan susun semula projek supaya pemeta dijana dengan baris baharu. Setelah ini selesai, mari lihat bagaimana pelaksanaan pemeta untuk LectureListMapper telah berubah (diserlahkan dalam huruf tebal bahagian yang kita perlukan):
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;
   }
}
Dan kini Mapstruct telah melaksanakan suntikan pemeta melalui pembina. Inilah yang kami cuba capai. Sekarang ujian kami akan berhenti menyusun, mari kemas kini dan dapatkan:
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());
   }
}
Sekarang, jika kita menjalankan ujian, semuanya akan berfungsi seperti yang diharapkan, kerana dalam LectureListMapperImpl kita lulus LectureMapper yang diperlukan... Kemenangan! Ia tidak sukar untuk anda, tetapi saya gembira: Rakan, semuanya seperti biasa, langgan akaun GitHub saya , ke akaun Telegram saya . Di sana saya menyiarkan hasil aktiviti saya, ada perkara yang sangat berguna) Saya terutamanya menjemput anda untuk menyertai kumpulan perbincangan saluran telegram . Kebetulan jika seseorang mempunyai soalan teknikal, mereka boleh mendapatkan jawapan di sana. Format ini menarik untuk semua orang, anda boleh membaca siapa yang tahu dan dapatkan pengalaman.

Kesimpulan

Sebagai sebahagian daripada artikel ini, kami berkenalan dengan produk yang diperlukan dan kerap digunakan seperti Mapstruct. Kami mengetahui apa itu, mengapa dan bagaimana. Menggunakan contoh sebenar, kami merasakan apa yang boleh dilakukan dan bagaimana ia boleh diubah. Kami juga melihat bagaimana untuk menyediakan suntikan kacang melalui pembina, supaya mungkin untuk menguji pemeta dengan betul. Rakan sekerja dari Mapstruct membenarkan pengguna produk mereka memilih dengan tepat cara menyuntik pemeta, yang pastinya kami mengucapkan terima kasih kepada mereka. TETAPI, walaupun pada hakikatnya Spring mengesyorkan menyuntik kacang melalui pembina, lelaki dari Mapstruct telah menetapkan suntikan melalui medan secara lalai. Kenapa begitu? Tiada jawapan. Saya mengesyaki mungkin ada sebab yang kita tidak tahu, dan itulah sebabnya mereka melakukannya dengan cara ini. Dan untuk mengetahui daripada mereka, saya mencipta isu GitHub pada repositori produk rasmi mereka.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION