MapStruct — это библиотека генерации кода, предназначенная для упрощения сопоставления между Java-компонентами. Это позволяет разработчикам автоматически генерировать код сопоставления во время компиляции с помощью процессора аннотаций после определения правил сопоставления. MapStruct следует принципу «соглашение важнее конфигурации». В большинстве случаев он может разумно обрабатывать распространенные сценарии сопоставления, не требуя от разработчиков написания громоздкой логики сопоставления.
MapStruct основан на спецификации Java JSR 269, которая позволяет обрабатывать аннотации во время компиляции. MapStruct считывает интерфейс сопоставления во время компиляции через определенный процессор аннотаций и генерирует соответствующий класс реализации. Во время этого процесса он анализирует методы сопоставления, объявленные в интерфейсе, и создает соответствующие вызовы методов получения и установки.
@Mapper
аннотация,Объявите методы, которые необходимо сопоставить.@Mapping
аннотация指定属性картографирование规则。Mappers.getMapper()
Метод получения экземпляра картографа,и вызовите метод сопоставления.преимущество:
недостаток:
MapStruct широко используется и признан в сообществе Java благодаря своей простоте, эффективности и безопасности типов. За счет сокращения повторяющегося шаблонного кода разработчики могут больше сосредоточиться на реализации бизнес-логики и повышают эффективность разработки.
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<!-- IntelliJ does not pick up the processor if it is not in the dependencies.
There is already an open issue for IntelliJ see https://youtrack.jetbrains.com/issue/IDEA-150621
-->
<scope>provided</scope>
</dependency>
Базовое сопоставление. Используя MapStruct, вы можете легко реализовать базовое сопоставление между двумя объектами Java Bean. Просто определите интерфейс преобразователя и используйте аннотации для указания исходного и целевого классов, и MapStruct сгенерирует класс реализации во время компиляции.
Entity
package com.artisan.mapstruct.entity;
import com.artisan.mapstruct.CarType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
}
package com.artisan.mapstruct;
/**
* @author artisan
*/
public enum CarType {
BMW(1, "BMW"),
FLL(2, "FLL");
private int code;
private String brand;
CarType(int code, String brand) {
this.code = code;
this.brand = brand;
}
public int getCode() {
return code;
}
public String getBrand() {
return brand;
}
// Как получить бренд на основе кода
public static String getBrandByCode(int code) {
for (CarType carType : CarType.values()) {
if (carType.getCode() == code) {
return carType.getBrand();
}
}
return null; // Если код не существует, верните ноль или другое значение по умолчанию.
}
}
Mapper
package com.artisan.mapstruct.entity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity.Car;
import com.artisan.mapstruct.entity.CarDto;
import com.artisan.mapstruct.entity.CarMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Базовое сопоставление атрибутов
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests1 {
@Test
public void testBasicTypeConvert() {
Car car = new Car("artisan", 7, CarType.BMW);
CarDto cardto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(car);
System.out.println(cardto);
Assertions.assertEquals(car.getNumberOfSeats(), cardto.getSeatCount());
}
}
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
// Содержит объект (Включая сложные типы или пользовательские типы)
private AnotherPojo anotherPojo;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AnotherPojo {
private String pa;
private long pb;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
Mapper
package com.artisan.mapstruct.entity2;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "anotherPojo.pa", target = "pa")
@Mapping(source = "anotherPojo.pb", target = "pb")
CarDto carToCarDto(Car car);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity2.Car;
import com.artisan.mapstruct.entity2.CarDto;
import com.artisan.mapstruct.entity2.CarMapper;
import com.artisan.mapstruct.entity2.AnotherPojo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* сложное сопоставление типов
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests2 {
@Test
public void testComplexConvert() {
Car car = new Car("artisan", 7, CarType.BMW ,new AnotherPojo("paValue",66L));
CarDto cardto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(car);
System.out.println(cardto);
Assertions.assertEquals(car.getAnotherPojo().getPa() , cardto.getPa());
Assertions.assertEquals(car.getAnotherPojo().getPb() , cardto.getPb());
}
}
MapStruct поддерживает использование выражений в картографах. Сложную логику сопоставления можно реализовать путем написания лямбда-выражений или ссылок на методы.
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private String brand;
private int numberOfSeats;
private CarType type;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
private String fullInfo;
}
Mapper
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
// В процессе сопоставления может возникнуть необходимость использовать пользовательскую логику. Карта Структура разрешить вам использовать Java Выражение для достижения этой цели
@Mapping(expression = "java(car.getMake() + ' ' + car.getBrand())", target = "fullInfo")
CarDto carToCarDto(Car car);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity3.Car;
import com.artisan.mapstruct.entity3.CarDto;
import com.artisan.mapstruct.entity3.CarMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Используйте выражения
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests3 {
@Test
public void testExpressConvert() {
Car car = new Car("artisan", "BMW", 7, CarType.BMW);
CarDto cardto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(car);
System.out.println(cardto);
Assertions.assertEquals(car.getMake() + ' ' + car.getBrand(), cardto.getFullInfo());
}
}
MapStruct позволяет определять в модулях отображения собственные методы для реализации сложной логики отображения. Например, вы можете определить метод для преобразования поля в исходном объекте и назначить его целевому объекту.
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
// Заводская дата , Тип строки
private String manufactureDate;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
// Заводская дата , Тип LocalDate
private LocalDate manufactureDate2;
}
Mapper
package com.artisan.mapstruct.entity4;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "manufactureDate", target = "manufactureDate2", qualifiedByName = "stringToLocalDate")
CarDto carToCarDto(Car car);
@Named("stringToLocalDate")
default LocalDate stringToLocalDate(String date) {
return LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
Это тоже нормально
package com.artisan.mapstruct.entity4;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "manufactureDate", target = "manufactureDate2")
CarDto carToCarDto(Car car);
default LocalDate stringToLocalDate(String date) {
return LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
Может быть реализован с использованием интерфейса Java по умолчанию.
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity4.Car;
import com.artisan.mapstruct.entity4.CarDto;
import com.artisan.mapstruct.entity4.CarMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Использовать собственный метод
*
* В некоторых случаях может потребоваться Пользовательское парламентская логика. Этого можно добиться, определив собственный метод в интерфейсе картографа.
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests4 {
@Test
public void testCustomConvert() {
Car car = new Car("artisan", 7, CarType.BMW, "2099-12-12");
CarDto cardto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(car);
System.out.println(cardto);
Assertions.assertEquals(car.getManufactureDate(), cardto.getManufactureDate2().toString());
}
}
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int numberOfSeats;
private String type;
}
Mapper
package com.artisan.mapstruct.entity5;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
List<CarDto> carsToCarDtos(List<Car> cars);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity5.Car;
import com.artisan.mapstruct.entity5.CarDto;
import com.artisan.mapstruct.entity5.CarMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
/**
* коллекция карт
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests5 {
@Test
public void testCollectionfConvert() {
Car car = new Car("artisan", 7, CarType.BMW);
Car car2 = new Car("artisan2", 9, CarType.FLL);
List carList = new ArrayList();
carList.add(car);
carList.add(car2);
List<CarDto> cardtos = CarMapper.INSTANCE.carsToCarDtos(carList);
cardtos.stream().forEach(System.out::println);
}
}
MapStruct поддерживает внедрение зависимостей и может использовать в сопоставителе сторонние библиотеки или платформы. Это облегчает использование других компонентов в процессе сопоставления объектов.
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
}
Mapper
componentModel = MappingConstants.ComponentModel.SPRING
package com.artisan.bootbeanutils.controller.ms;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface CarMapper {
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
Модульное тестирование
package com.artisan.bootbeanutils.controller;
import com.artisan.bootbeanutils.controller.ms.Car;
import com.artisan.bootbeanutils.controller.ms.CarDto;
import com.artisan.bootbeanutils.controller.ms.CarMapper;
import com.artisan.bootbeanutils.controller.ms.CarType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@RestController
public class TestController {
@Autowired
private CarMapper carMapper;
@GetMapping("/test")
public String test() {
CarDto carDto = carMapper.carToCarDto(new Car("artisan", 8, CarType.BMW));
return carDto.toString();
}
}
автоматический впрыск CarMapper
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
}
Mapper
package com.artisan.mapstruct.entity7;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "seatCount", target = "numberOfSeats")
void updateCarFromDTO(CarDto personDto, @MappingTarget Car car);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity7.Car;
import com.artisan.mapstruct.entity7.CarDto;
import com.artisan.mapstruct.entity7.CarMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* MapStruct Также может использоваться для обновления существующего объекта вместо создания нового.
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests7 {
@Test
public void testUpdate() {
// Имитировать существование автомобильного объекта
Car car = new Car("artisan", 9, CarType.BMW);
CarDto carDto = new CarDto("artisanDto", 100, CarType.FLL.getBrand());
// Используется для обновления существующего объекта вместо создания нового.
CarMapper.INSTANCE.updateCarFromDTO(carDto, car);
System.out.println(carDto);
System.out.println(car);
}
}
MapStruct поддерживает полиморфное сопоставление. Определив интерфейс преобразователя, несколько объектов подкласса могут быть сопоставлены с объектом родительского класса. Это очень полезно при работе с сопоставлениями объектов со сложными отношениями наследования.
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AnotherPojo {
private String pa;
}
package com.artisan.mapstruct.entity8;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {
private String make;
private int seatCount;
private String type;
private String pa;
}
Mapper
package com.artisan.mapstruct.entity8;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author маленький мастер
* @version 1.0
* @mark: show me the code , change the world
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "car.numberOfSeats", target = "seatCount")
@Mapping(source = "anotherPojo.pa", target = "pa")
CarDto carToCarDto(Car car, AnotherPojo anotherPojo);
}
Модульное тестирование
package com.artisan.mapstruct;
import com.artisan.bootbeanutils.BootBeanUtilsApplication;
import com.artisan.mapstruct.entity8.AnotherPojo;
import com.artisan.mapstruct.entity8.Car;
import com.artisan.mapstruct.entity8.CarDto;
import com.artisan.mapstruct.entity8.CarMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* картографирование с несколькими источниками
* <p>
* Может сопоставлять объект из нескольких источников с одним целевым объектом.
*/
@SpringBootTest(classes = BootBeanUtilsApplication.class)
class MapStructApplicationTests8 {
@Test
public void MultiSourceConvert() {
Car car = new Car("artisan", 7, CarType.BMW);
AnotherPojo anotherPojo = new AnotherPojo("paValue");
CarDto cardto = CarMapper.INSTANCE.carToCarDto(car, anotherPojo);
System.out.println(car);
System.out.println(cardto);
Assertions.assertEquals(car.getNumberOfSeats(), cardto.getSeatCount());
Assertions.assertEquals(anotherPojo.getPa(), cardto.getPa());
}
}
Конечно, вы также можете нажать здесь: Quick Guide to MapStruct Есть несколько примеров, которые не описаны, зайдите и посмотрите.
Performance of Java Mapping Frameworks
https://github.com/eugenp/tutorials/tree/master/performance-tests