expression
指定静态方法expression
指定spring-bean的实例方法从功能上来讲,mapstruct
是一款类似于BeanUtils.copyProperties(Object source, Object target)
一样,实现对象属性值复制的;从实现上来讲,mapstruct
是一款类似于lombok
,基于你给出的方法入参出参模型及方法、类上的相关辅助注解,直接在编译时生成对应的属性值转换实现类。因为mapstruct
是使用自动生成的代码实现的对象属性值赋值(而不是像BeanUtils
一样采用反射获取值赋值),所以性能更快、效率更高。更多详见官网。
...
<properties>
<org.mapstruct.version>1.4.2.Finalorg.mapstruct.version>
properties>
...
<dependencies>
<dependency>
<groupId>org.mapstructgroupId>
<artifactId>mapstructartifactId>
<version>${org.mapstruct.version}version>
dependency>
dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
path>
<path>
<groupId>org.mapstructgroupId>
<artifactId>mapstruct-processorartifactId>
<version>${org.mapstruct.version}version>
path>
annotationProcessorPaths>
<showWarnings>trueshowWarnings>
<compilerArgs>
<arg>
-Amapstruct.verbose=true
arg>
compilerArgs>
configuration>
plugin>
plugins>
build>
...
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface CarMapper {
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
}
或
@Mapper
public abstract class CarMapper {
@Mapping(target = "seatCount", source = "numberOfSeats")
public abstract CarDto carToCarDto(Car car);
}
public static void main(String[] args) throws IOException {
// source对象
Car car = new Car();
car.setNumberOfSeats(123);
car.setColor("白色");
// 获取mapper实例
CarMapper mapper = Mappers.getMapper(CarMapper.class);
// 调用对应方法,实现转换
CarDto carDto = mapper.carToCarDto(car);
// 输出: CarDto(seatCount=123, color=白色)
System.out.println(carDto);
}
注:获取Mapper实例的方式,取决于
@Mapper(componentModel=xxx)
中,componentModel
的模式:
default
:the mapper uses no component model, instances are typically retrieved viaMappers.getMapper(Class)
cdi
模式:the generated mapper is an application-scoped CDI bean and can be retrieved via@Inject
spring
模式:the generated mapper is a Spring bean and can be retrieved via@Autowired
jsr330
模式:the generated mapper is annotated with @javax.inject.Named and @Singleton, and can be retrieved via@Inject
default时,可通过 Mappers.getMapper(Class)
获取实例
@Mapper
//等价于@Mapper(componentModel = "default")
public interface CarMapper {
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
}
获取实例
// 获取mapper实例
CarMapper mapper1 = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@69e308c6
System.out.println(mapper1);
CarMapper mapper2 = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@1a1ed4e5
System.out.println(mapper2);
注:
Mappers.getMapper(Class)
获取实例时,每次都是获取到一个新的实例。所以如果非要使用Mappers.getMapper(Class)
的话,需要尽量避免重复创建,可以使用下述方式:@Mapper public interface CarMapper { /** 全局使用这一个对象 */ CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car); }
或者
@Mapper public abstract class BusMapper { /** 全局使用这一个对象 */ public final BusMapper INSTANCE = Mappers.getMapper(BusMapper.class); @Mapping(target = "seatCount", source = "numberOfSeats") public abstract CarDto carToCarDto(Car car); }
spring模式时,可通过 Mappers.getMapper(Class)
或@Autowired
或@Resource
等方式获取实例
@Mapper(componentModel = "spring")
public interface CarMapper {
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
}
获取实例
@SpringBootApplication
public class SpringBootDemoApplication implements ApplicationRunner {
@Autowired
private CarMapper carMapper1;
@Resource
private CarMapper carMapper2;
public static void main(String[] args) throws IOException {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
// 获取mapper实例
CarMapper mapper = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@f2c488
System.out.println(mapper);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@54acff7d
System.err.println(carMapper1);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@54acff7d
System.err.println(carMapper2);
}
}
使用较少,不作介绍,详见官网。
source是转化源,target是转化目标
@Mapper(componentModel = "spring")
public interface CarMapper {
/** Car为source, CarDto为target */
CarDto carToCarDto(Car car);
}
注:target是一个新创建的对象。
@Mapper(componentModel = "spring")
public interface CarMapper {
/** Car为source, CarDto为target */
void carToCarDto(Car car, @MappingTarget CarDto carDto);
}
通过@Mapping
的source
与target
指定字段名映射
@Mapper(componentModel = "spring")
public interface CarMapper {
/** 指定不同字段名间的映射 */
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
}
注:默认的,mapstruct只会转换字段名称相同的字段。
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 当目标字段值最后为null前, 设置其默认值为100
*/
@Mapping(target = "seatCount", defaultValue = "100")
CarDto carToCarDto(Car car);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 指定常量值
*/
@Mapping(target = "seatCount", constant = "100")
CarDto carToCarDto(Car car);
}
@Mapper
public interface CarMapper {
@Mapping(target = "length", source = "carLength")
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
}
或
@Mapper
public interface CarMapper {
@Mappings(value = {
@Mapping(target = "length", source = "carLength"),
@Mapping(target = "seatCount", source = "numberOfSeats")
})
CarDto carToCarDto(Car car);
}
可通过{字段名}
.{字段名}
.{字段名}
的形式进行多级定位
public class multiLevel_field {
public static void main(String[] args) {
Car car = new Car();
car.setColor("yellow");
car.setNumberOfSeats(10);
car.setPerson(new Person("张三", 28));
// 输出:multiLevel_field.CarDto(color=yellow, seatCount=10, personName=张三, personAge=28)
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(carDto);
// 输出:multiLevel_field.Car(color=yellow, numberOfSeats=10, person=multiLevel_field.Person(name=张三, age=28))
car = CarMapper.INSTANCE.carDtoToCar(carDto);
System.out.println(car);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(target = "personName", source = "person.name")
@Mapping(target = "personAge", source = "person.age")
CarDto carToCarDto(Car c);
@Mapping(target = "numberOfSeats", source = "seatCount")
@Mapping(target = "person.name", source = "personName")
@Mapping(target = "person.age", source = "personAge")
Car carDtoToCar(CarDto c);
}
@Data
public static class CarDto {
private String color;
private Integer seatCount;
private String personName;
private Integer personAge;
}
@Data
public static class Car {
private String color;
private Integer numberOfSeats;
private Person person;
}
@Data
@AllArgsConstructor
public static class Person {
private String name;
private Integer age;
}
}
通过多级定位,mapstruct可以实现多个对象转一个对象
public class multi_to_one {
public static void main(String[] args) {
Car car = new Car();
car.setColor("green");
car.setNumberOfSeats(10);
Person person = new Person("张三", 28);
// 输出:multi_to_one.CarDto(color=green, seatCount=10, personName=张三, personAge=28)
CarDto carDto1 = CarMapper.INSTANCE.generateCarDto1(car, person);
System.out.println(carDto1);
// 输出:multi_to_one.CarDto(color=green, seatCount=10, personName=张三, personAge=28)
CarDto carDto2 = new CarDto();
CarMapper.INSTANCE.generateCarDto2(car, person, carDto2);
System.out.println(carDto2);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 因为Car和Person中所有的字段里,只有一个与CarDto#color匹配,所以这里可以省略该@Mapping。
* 但如果有多个同事匹配时,需要指定@Mapping;否则编译时mapstruct会报错
*/
@Mapping(target = "seatCount", source = "c.numberOfSeats")
@Mapping(target = "personName", source = "p.name")
@Mapping(target = "personAge", source = "p.age")
CarDto generateCarDto1(Car c, Person p);
/**
* 等价于
*/
@Mapping(target = "seatCount", source = "c.numberOfSeats")
@Mapping(target = "personName", source = "p.name")
@Mapping(target = "personAge", source = "p.age")
void generateCarDto2(Car c, Person p, @MappingTarget CarDto carDto);
}
@Data
public static class CarDto {
private String color;
private Integer seatCount;
private String personName;
private Integer personAge;
}
@Data
public static class Car {
private String color;
private Integer numberOfSeats;
}
@Data
@AllArgsConstructor
public static class Person {
private String name;
private Integer age;
}
}
mapstruct是基于getter/setter方法读写字段,因为java getter/setter是小驼峰式命名,所以对于字段的首字母的大小写不敏感,能赋值成功,如下面的color与Color,但是对于其它位置的大小写敏感,不能赋值成功,如下面的size与siZe;对于is打头的boolean型字段,lombok生成的getter/setter是回保留原有的is的,所以mapstruct解析后girl与isGirl是不匹配的,除非你自己额外添加对应的getter/setter,如下面的boy与isBoy的getter/setter都是getBoy/setBoy。
public class case_sensitive {
public static void main(String[] args) {
Car car = new Car();
car.setColor("yellow");
car.setSiZe(10);
car.setBoy(true);
car.setIsGirl(false);
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
// 输出 case_sensitive.CarDto(color=yellow, size=null, boy=true, girl=null)
System.out.println(carDto);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDto carToCarDto(Car car);
}
@Data
public static class CarDto {
private String color;
private Integer size;
private Boolean boy;
/** lombok生成的getter/setter 是 getGirl/setGirl */
private Boolean girl;
public Boolean getBoy() {
return boy;
}
public void setBoy(Boolean boy) {
this.boy = boy;
}
}
@Data
public static class Car {
private String Color;
private Integer siZe;
private Boolean isBoy;
/** lombok生成的getter/setter 是 getIsGirl/setIsGirl */
private Boolean isGirl;
public Boolean getBoy() {
return isBoy;
}
public void setBoy(Boolean boy) {
this.isBoy = boy;
}
}
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 忽略字段 num
*/
@Mapping(target = "num", ignore = true)
CarDto carCarDto(Car c);
}
public class enum_string {
public static void main(String[] args) {
Car car = new Car();
car.setColor(ColorEnum.RED);
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
// 输出:enum_string.CarDto(color=RED)
System.out.println(carDto);
carDto = new CarDto();
carDto.setColor("GREEN");
car = CarMapper.INSTANCE.carDtoToCar(carDto);
// 输出:enum_string.Car(color=GREEN)
System.out.println(car);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDto carToCarDto(Car car);
Car carDtoToCar(CarDto car);
}
@Data
public static class CarDto {
private String color;
}
@Data
public static class Car {
private ColorEnum color;
}
public static enum ColorEnum {
RED,
GREEN
}
}
@ValueMapping指定不同枚举值(即:Enum#name())之间的转换
public class enum_enum {
public static void main(String[] args) {
Car car = new Car();
car.setColor1(ColorBEnum.RED);
car.setColor2(ColorBEnum.GREEN);
car.setColor3(ColorBEnum.VIOLET);
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
// 输出:enum_enum.CarDto(color1=RED, color2=GREEN, color3=PURPLE)
System.out.println(carDto);
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* - 相同的可以不指定,如这里的RED就可以不指定
* - 不同的枚举值需要指定,如这里的 PURPLE与VIOLET
*/
@ValueMapping(target = "RED", source = "RED")
@ValueMapping(target = "PURPLE", source = "VIOLET")
CarDto carToCarDto(Car car);
}
@Data
public static class CarDto {
private ColorAEnum color1;
private ColorAEnum color2;
private ColorAEnum color3;
}
@Data
public static class Car {
private ColorBEnum color1;
private ColorBEnum color2;
private ColorBEnum color3;
}
public static enum ColorAEnum {
RED,
PURPLE,
GREEN
}
public static enum ColorBEnum {
RED,
VIOLET,
GREEN
}
}
提示:这里只罗列了常见的自动转换方式,实际上mapstruct内置支持了很多自动转换,不限于jdk内的类
提示:放心大胆的用即可,若两个类型之间不支持转换,那么在项目编译时mapstruct会报错提示的
基础类型 <=> 包装类型,会自动转换
- 当 包装类型 => 基础类型 时,若包装类型为null,则在mapstruct转换时(即:程序编译时)会报错
数值类型(int、Integer、long、Long等等)之间,会自动转换
- 在大数转化为小数时,可能导致精度丢失
- 当大的数据类型转化为小的数据类型时,若大的数据类型的值超过了小的数据类型的上限,那么可能转化失准,如:long类型的Long.MAX_VALUE值,转化为int类型时,得到的int的值为-1
基础类型(包括它们的包装类型)和 String 之间,会自动转换
- 数值 => String时,可以指定格式(格式规则同
java.text.DecimalFormat
),如:public static void main(String[] args) { List<String> prices = CarMapper.INSTANCE.prices1(Lists.newArrayList(1, 2, 3)); // 输出:[$1.00, $2.00, $3.00] System.out.println(prices); prices = CarMapper.INSTANCE.prices2(Lists.newArrayList(1, 2, 3)); // 输出:[1E0, 2E0, 3E0] System.out.println(prices); } @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); @IterableMapping(numberFormat = "$#.00") List<String> prices1(List<Integer> prices); @IterableMapping(numberFormat = "#.##E0") List<String> prices2(List<Integer> prices); }
枚举 <=> 字符串,会自动转换
- enum => string,通过方法Enum#name
- string => enum,通过方法Enum#valueOf
大数据类型 <=> 基础类型(包括它们的包装类型) <=> String
- 大数据类型包括
java.math.BigInteger
,java.math.BigDecimal
- 当 包装类型 => 基础类型 时,若包装类型为null,则在mapstruct转换时(即:程序编译时)会报错
- 在大数转化为小数时,可能导致精度丢失
- 当大的数据类型转化为小的数据类型时,若大的数据类型的值超过了小的数据类型的上限,那么可能转化失准,如:long类型的Long.MAX_VALUE值,转化为int类型时,得到的int的值为-1
- 数值 => String时,可以指定格式(格式规则同
java.text.DecimalFormat
)
java.util.Calendar <=> java.sql.*<=> java.util.Date <=> String <=> java.time.*
- java.time.*包括
- java.time.ZonedDateTime
- java.time.LocalDateTime
- java.time.LocalDate
- java.time.LocalTime
- java.time.Instant
- java.time.Duration
- java.time.Period
- …
- java.sql.*包括
- java.sql.Time
- java.sql.Date
- java.sql.Timestamp
- …
- 指定格式的方式,同
java.text.SimpleDateFormat
public static void main(String[] args) { List<String> prices = CarMapper.INSTANCE.convert(Lists.newArrayList(new Date())); // 输出:[2022-03-28] System.out.println(prices); } @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); @IterableMapping(dateFormat = "yyyy-MM-dd") List<String> convert(List<Date> dates); }
java.util.Currency <=> String
货币 <=> 字符串
list <=> set、set <=> set、list <=> list等集合之间可进行相互转换
- 可相互转换的集合有
接口类型 mapstruct选择作为实现的类 Iterable ArrayList Collection ArrayList List ArrayList Set HashSet SortedSet TreeSet NavigableSet TreeSet Map HashMap SortedMap TreeMap NavigableMap TreeMap ConcurrentMap ConcurrentHashMap ConcurrentNavigableMap ConcurrentSkipListMap … …
- 简单示例list <=> set
public static void main(String[] args) { // 输出:[1, 2, 3] System.out.println(CarMapper.INSTANCE.convert1(Sets.newHashSet(1, 2, 3))); // 输出:[1, 2, 3] System.out.println(CarMapper.INSTANCE.convert2(Lists.newArrayList(1, 2, 3, 1))); } @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); List<String> convert1(Set<Integer> items); Set<String> convert2(List<Integer> items); }
更多详见官网
@Mapper
的uses
可以指定复用spring容器中已有的spring-bean。
@SpringBootApplication
@SuppressWarnings("all")
public class use_exist_mapper implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(use_exist_mapper.class, args);
}
@Autowired
CarMapper carMapper;
@Override
public void run(ApplicationArguments args) throws Exception {
Car car = new Car();
car.setColor("yellow");
car.setNumberOfSeats(10);
car.setPersonList(Lists.newArrayList(new Person(true), new Person(false)));
CarDto carDto = carMapper.carToCarDto1(car);
// 输出:use_exist_mapper.CarDto(color=yellow, seatCount=10, personList=[use_exist_mapper.PersonDto(isBoy=true), use_exist_mapper.PersonDto(isBoy=false)])
System.out.println(carDto);
carDto = carMapper.carToCarDto2(car);
// 输出:use_exist_mapper.CarDto(color=yellow, seatCount=10, personList=[use_exist_mapper.PersonDto(isBoy=true), use_exist_mapper.PersonDto(isBoy=false)])
System.out.println(carDto);
}
@Mapper(componentModel = "spring", uses = {StringMapperA.class})
public interface CarMapper {
/**
* 方式一:直接默认使用匹配到的StringMapperA#personToPersonDto方法进行Person => PersonDto 的转换
*/
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto1(Car car);
/**
* 方式二:直接指定转换方法.
*
* 注:此方式部分ban本又bug,目前可能不够通用。
*/
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(target = "personList", expression = "java(stringMapperA.xyz(car123.getPersonList()))")
CarDto carToCarDto2(Car car123);
}
@Component
public static class StringMapperA {
public PersonDto personToPersonDto(Person person) {
return new PersonDto(person.getBoy());
}
public List<PersonDto> xyz(List<Person> list) {
return list.stream().map(p -> new PersonDto(p.getBoy())).collect(Collectors.toList());
}
}
@Data
public static class CarDto {
private String color;
private Integer seatCount;
private List<PersonDto> personList;
}
@Data
public static class Car {
private String color;
private Integer numberOfSeats;
private List<Person> personList;
}
@Data
@AllArgsConstructor
public static class PersonDto {
private Boolean isBoy;
}
@Data
@AllArgsConstructor
public static class Person {
private Boolean boy;
}
}
expression
指定静态方法格式为:
@Mapping(target = "目标字段名", expression = "java({全类名}.{静态方法名}({形参名}.{getter方法})")
public class expression_point_static_method {
public static void main(String[] args) {
Car car = new Car();
car.setColor("yellow");
car.setNumberOfSeats(1);
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
// 输出:expression_point_static_method.CarDto(color=yellow, seatCount=2)
System.out.println(carDto);
}
@Mapper
public static abstract class CarMapper {
public static final CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/** {@link CarMapper}全类名 */
public static final String CLASS_LONG_NAME = "com.example.mapstruct.obtain_instance_test.expression_point_static_method.CarMapper";
@Mapping(target = "seatCount",
expression = "java(" + CLASS_LONG_NAME + ".addOne(car123.getNumberOfSeats()))")
public abstract CarDto carToCarDto(Car car123);
public static Integer addOne(Integer a) {
return ++a;
}
}
@Data
public static class CarDto {
private String color;
private Integer seatCount;
}
@Data
public static class Car {
private String color;
private Integer numberOfSeats;
}
}
优化: 直接写全类名,后期不好维护,可以采用@Mapper的import
能力,这样一来,expression
中只需要写简类名即可,如:
@Mapper(imports = {CarMapper2.class})
public static abstract class CarMapper2 {
public static final CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(target = "seatCount",
expression = "java(CarMapper2.addOne(car123.getNumberOfSeats()))")
public abstract CarDto carToCarDto(Car car123);
public static Integer addOne(Integer a) {
return ++a;
}
}
expression
指定spring-bean的实例方法提示:mapstruct的
@Mapper(uses = {xxx.class})
也能达到注入xxx依赖的效果,但是部分版本有bug,使用不稳定,所以可以直接利用下面抽象类的方式使用
@SpringBootApplication
@SuppressWarnings("all")
public class expression_point_instance_method implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(expression_point_instance_method.class, args);
}
@Autowired
CarMapper123 carMapper123;
@Override
public void run(ApplicationArguments args) throws Exception {
Car car = new Car();
car.setColor("yellow");
car.setNumberOfSeats(10);
car.setPersonList(Lists.newArrayList(new Person(true), new Person(false)));
CarDto carDto = carMapper123.carToCarDto2(car);
// 输出:expression_point_instance_method.CarDto(color=yellow, seatCount=10, personList=[expression_point_instance_method.PersonDto(isBoy=true), expression_point_instance_method.PersonDto(isBoy=false)])
System.out.println(carDto);
}
@Mapper(componentModel = "spring")
public static abstract class CarMapper123 {
@Autowired
protected StringMapperB stringMapperB;
/**
* 直接指定转换方法
*
* 注:如果调用的xyz是this里面的方法,那么还可以简写:@Mapping(target = "personList", expression = "java(xyz(car123.getPersonList()))")
*/
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(target = "personList", expression = "java(stringMapperB.xyz(car123.getPersonList()))")
public abstract CarDto carToCarDto2(Car car123);
}
@Component
public static class StringMapperB {
public List<PersonDto> xyz(List<Person> list) {
return list.stream().map(p -> new PersonDto(p.getBoy())).collect(Collectors.toList());
}
}
@Data
public static class CarDto {
private String color;
private Integer seatCount;
private List<PersonDto> personList;
}
@Data
public static class Car {
private String color;
private Integer numberOfSeats;
private List<Person> personList;
}
@Data
@AllArgsConstructor
public static class PersonDto {
private Boolean isBoy;
}
@Data
@AllArgsConstructor
public static class Person {
private Boolean boy;
}
}
当mapstruct要进行Type1 => Type2转换时,首先会先去寻找是否已存在现成的Type1 => Type2的方法,如果已存在,那么直接利用该方法进行对应类型的转换;如果不存在,则自动生成。
判断一个方法是否是Type1 => Type2的转换方法,只需满足:
- 该方法的入参类型要为Type1,出参类型要为Type2
- 该方法的方法名.equalsIgnoreCase(“Type1ToType2”)
而提供已存在的转换方法又可分为两种:
- 第一种:已存在的其他mapper方法(由mapstruct生成)
- 第二种:已存在的自己写的mapper方法(由程序员自己写)
public class handWritten_mapping_logic {
public static void main(String[] args) {
Car car = new Car();
car.setPersonList(Lists.newArrayList(new Person(true), new Person(false)));
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
// {"color":"yellow","personList":[{"isBoy":true},{"isBoy":false}]}
System.out.println(JSON.toJSONString(carDto));
carDto = CarMapper2.INSTANCE.carToCarDto(car);
// {"color":"yellow","personList":[{"isBoy":true},{"isBoy":false}]}
System.out.println(JSON.toJSONString(carDto));
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* mapstruct推导过程:
* 1. Car中的List => CarDto中的List
* 2. 识别泛型,即:Person => PersonDto
* 3. 发现存在方法 PersonDto personToCarPersonDto(Person person), 则直接调用该方法进行Person => PersonDto
*/
CarDto carToCarDto(Car car);
/**
* 示例 第一种:已存在的其他mapper方法(由mapstruct生成)
*/
@Mapping(source = "boy", target = "isBoy")
PersonDto personToPersonDto1(Person person);
}
@Mapper
public interface CarMapper2 {
CarMapper2 INSTANCE = Mappers.getMapper(CarMapper2.class);
CarDto carToCarDto(Car car);
/**
* 示例 第二种:已存在的自己写的mapper方法(由程序员自己写)
*/
default PersonDto personToPersonDto1(Person person) {
return new PersonDto(person.getBoy());
}
}
@Data
public static class CarDto {
private List<PersonDto> personList;
}
@Data
public static class Car {
private List<Person> personList;
}
@Data
@AllArgsConstructor
public static class PersonDto {
private Boolean isBoy;
}
@Data
@AllArgsConstructor
public static class Person {
private Boolean boy;
}
}
mapstruct入门知识学习完毕 !