知识 - MapStructPlus
上一个 MapStruct 文档 介绍了基本的 JavaBean 转换工具,但是需要额外新建 Convertor 类将两个 JavaBean 转换,下面介绍 MapStructPlus 工具,
这是社区大佬基于 MapStruct 开发的,内嵌 Mapstruct,使用 注解 的时候就可以将 JavaBean 直接进行转换,和 Mapstruct 完全兼容,如果之前已经使用 Mapstruct,可以无缝替换依赖。
# 引入依赖
<properties>
<java.version>JDK 版本,如 17</java.version>
<mapstruct-plus.version>最新版本,如 1.3.5</mapstruct-plus.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
</properties>
<dependencies>
<!-- mapstructPlus 依赖 -->
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
如果使用了 Lombok,两者是有冲突的,那么解决的方案如下:
lombok 1.18.16 之前:
<properties>
<java.version>JDK 版本,如 17</java.version>
<mapstruct-plus.version>最新版本,如 1.3.5</mapstruct-plus.version>
<lombok.version>最新版本,如 1.18.30</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<!-- 插件版本 -->
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
</properties>
<dependencies>
<!-- mapstructPlus 依赖 -->
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 解决 MapStruct 和 Lombok 冲突 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
lombok 1.18.16 及之后:
<properties>
<java.version>JDK 版本,如 17</java.version>
<mapstruct-plus.version>最新版本,如 1.3.5</mapstruct-plus.version>
<lombok.version>最新版本,如 1.18.30</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<!-- 插件版本 -->
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
</properties>
<dependencies>
<!-- mapstructPlus 依赖 -->
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 解决 MapStruct 和 Lombok 冲突 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.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>
</plugins>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
由于 MapstructPlus 其已经内嵌 Mapstruct,为了防止不同版本之间的差异,请不要再引入 Mapstruct 相关依赖。
# 简单使用
假设有两个类 UserDto
和 User
,我们目的是想将 UserDto
的属性转给 User
。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String username;
private int age;
private boolean young;
}
2
3
4
5
6
7
8
UserDto 类上加上 @AutoMapper
注解,然后 target 指定为要转化的对方类
@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = User.class)
public class UserDto {
private String username;
private int age;
private boolean young;
}
2
3
4
5
6
7
8
9
此时我们认为 UserDto 是 Source 类,User 是 Target 类。
测试
@SpringBootTest
public class QuickStartTest {
private static Converter converter = new Converter();
public static void main(String[] args) {
UserDto userDto = new UserDto();
userDto.setUsername("jack");
userDto.setAge(23);
userDto.setYoung(false);
User user = converter.convert(userDto, User.class);
System.out.println(user); // UserDto{username='jack', age=23, young=false}
assert user.getUsername().equals(user.getUsername());
assert user.getAge() == user.getAge();
assert user.isYoung() == user.isYoung();
UserDto newUser = converter.convert(user, UserDto.class);
System.out.println(newUser); // User{username='jack', age=23, young=false}
assert user.getUsername().equals(newUser.getUsername());
assert user.getAge() == newUser.getAge();
assert user.isYoung() == newUser.isYoung();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以看到,@AutoMapper
注解不仅对 UserDto 到 User 的转换生效,也对 User 到 UserDto 的转换生效。
# 使用指南
MapStructPlus 核心就是 @AutoMapper
注解,下面介绍该注解的更多使用指南。
# 简单转换
要实现两个类之间的转换,只需要在其中一个类上增加注解 @AutoMapper
,配置 target
属性,指定目标类即可。
Car
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
private String name;
private String type;
}
2
3
4
5
6
7
8
9
CarDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
private String name;
private String type;
}
2
3
4
5
6
7
8
测试
@SpringBootTest
public class SimpleConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
Car car = new Car().setName("宝马").setType("小轿车");
CarDto carDto = this.converter.convert(car, CarDto.class);
System.out.println("carDto = " + carDto); // carDto = CarDto(name=宝马, type=小轿车)
}
}
2
3
4
5
6
7
8
9
10
11
12
可以看编译后的结果
该例子表示,会生成 Car 转换为 CarDto 的接口 CarToCarDtoMapper 及实现类 CarToCarDtoMapperImpl。在生成的转换代码中,源类型(Car)的所有可读属性将被复制到目标属性类型(CarDto)的相应属性中。
当一个属性与它的目标实体对应物具有相同的名称时,将会被隐式映射。
除此之外,MapstructPlus 会根据当前的默认规则,生成 CarDto 转换为 Car 的接口 CarDtoToCarMapper 及实现类 CarDtoToCarMapperImpl。如果不想生成该转换逻辑的话,可以通过注解的 reverseConvertGenerate 属性来配置。
如:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class, reverseConvertGenerate = false)
public class Car {
private String name;
private String type;
}
2
3
4
5
6
7
8
9
这样就不会生成 CartDto 到 Cart 的转换,也就是:
this.converter.convert(carDto, Car.class);
不会转换成功。
# 对象的属性自动转换
当要转换的类中,存在自定义类时,会自动寻找该类型的转换方法。
例如,分别有两组对象模型:汽车(Car
)和座椅(SeatConfiguration
),其中 Car
依赖于 SeatConfiguration
。
分别对应对象如下:
Car、SeatConfiguration
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
private String name;
private String type;
private SeatConfiguration seatConfiguration;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = SeatConfigurationDto.class)
public class SeatConfiguration {
private String name;
private Integer price;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CarDto、SeatConfigurationDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
private String name;
private String type;
private SeatConfigurationDto seatConfiguration;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SeatConfigurationDto {
private String name;
private Integer price;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
测试
@SpringBootTest
public class ObjectConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
SeatConfiguration seatConfiguration = new SeatConfiguration().setName("真皮座椅").setPrice(5000);
Car car = new Car().setName("宝马").setType("小轿车").setSeatConfiguration(seatConfiguration);
CarDto carDto = this.converter.convert(car, CarDto.class);
System.out.println("carDto = " + carDto);
// carDto = CarDto(name=宝马, type=小轿车, seatConfiguration=SeatConfiguration(name=真皮座椅, price=5000))
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自定义属性转换
当两个类中属性存在不一致的场景时,例如名称、类型等不一致,可以进行自定义转换,通过在属性上面添加 @AutoMapping
,来配置映射规则。
# 不同属性名称映射
@AutoMapping
注解中,提供了 target
属性,可以配置当前属性与目标类中 target
属性之间映射。
例如,Book
转换为 BookDto
时,name
属性与 bookName
属性相对应:
Book
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = BookDto.class)
public class Book {
@AutoMapping(target = "bookName") // target 类的属性名
private String name;
}
2
3
4
5
6
7
8
9
BookDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BookDto {
private String bookName;
}
2
3
4
5
6
7
测试
@SpringBootTest
public class AttributeConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
Book book = new Book().setName("三国演义");
BookDto bookDto = this.converter.convert(book, BookDto.class);
System.out.println(bookDto); // BookDto(bookName=三国演义)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
@AutoMapping
注解中还提供 source
方法,该配置默认取当前属性的名称,之所以可以配置,是为了适应一种场景,当前类的某个属性,其内部的属性,转换为目标中的属性字段,则可以通过当前属性来配置。
Goods、Sku
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = GoodsVo.class, reverseConvertGenerate = false)
public class Goods {
@AutoMapping(source = "sku.price", target = "price") // price 是 GoodsVo 的属性名字
private Sku sku;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Sku {
private String name;
private Integer price;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GoodsDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class GoodsVo {
private Integer price;
}
2
3
4
5
6
7
测试
@SpringBootTest
public class AttributeConvertTest {
private Converter converter = new Converter();
@Test
public void test2() {
Sku sku = new Sku().setName("手机").setPrice(4999);
Goods goods = new Goods().setSku(sku);
GoodsVo goodsVo = this.converter.convert(goods, GoodsVo.class);
System.out.println(goodsVo); // GoodsVo(price=4999)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 指定时间格式转换
当时间类型(例如:Date
、LocalDateTime
、LocalDate
等等)需要和 String
通过指定时间格式进行转换时,可以通过 @AutoMapping
中的 dateFormat
来配置:
Order
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = OrderEntity.class)
public class Order {
@AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime orderTime;
@AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
private Date createTime;
@AutoMapping(target = "orderDate", dateFormat = "yyyy-MM-dd")
private String date;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OrderEntity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = Order.class)
public class OrderEntity {
@AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
private String orderTime;
@AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
private String createTime;
@AutoMapping(target = "date", dateFormat = "yyyy-MM-dd")
private LocalDate orderDate;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
测试
@SpringBootTest
public class TimeConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
Order order = new Order().setOrderTime(LocalDateTime.now()).setCreateTime(new Date()).setDate("2023-07-23");
OrderEntity orderEntity = this.converter.convert(order, OrderEntity.class);
System.out.println(orderEntity); // OrderEntity(orderTime=2023-07-23 22:21:00, createTime=2023_07_23 22:21:00, orderDate=2023-07-23)
OrderEntity orderEntity1 = new OrderEntity().setOrderTime("2023-07-23 22:00:00").setCreateTime("2023_07_23 22:21:00").setOrderDate(LocalDate.now());
Order order1 = this.converter.convert(orderEntity1, Order.class); // Order(orderTime=2023-07-23T22:00, createTime=Sun Jul 23 22:21:00 CST 2023, date=2023-07-23)
System.out.println(order1);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 指定数字格式转换
当数字类型(例如:int
/Integer
等数字基本类型及包装类、BigDecimal
)和 String
之间的转换需要指定数字格式,可以通过 @AutoMapping
的 numberFormat
来配置。
该格式需要 java.text.DecimalFormat
所支持
Order
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = OrderEntity.class)
public class Order {
@AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime orderTime;
@AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
private Date createTime;
@AutoMapping(target = "orderDate", dateFormat = "yyyy-MM-dd")
private String date;
@AutoMapping(numberFormat = "$0.00")
private BigDecimal orderPrice;
@AutoMapping(numberFormat = "$0.00")
private Integer goodsNum;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
OrderEntity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = Order.class)
public class OrderEntity {
@AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
private String orderTime;
@AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
private String createTime;
@AutoMapping(target = "date", dateFormat = "yyyy-MM-dd")
private LocalDate orderDate;
@AutoMapping(numberFormat = "$0.00")
private String orderPrice;
@AutoMapping(numberFormat = "$0.00")
private String goodsNum;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
测试
@SpringBootTest
public class NumberConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
Order order = new Order().setOrderPrice(BigDecimal.valueOf(998.99)).setGoodsNum(10000);
OrderEntity orderEntity = this.converter.convert(order, OrderEntity.class);
System.out.println(orderEntity); // OrderEntity(orderTime=null, createTime=null, orderDate=null, orderPrice=$998.99, goodsNum=$10000.00)s
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 忽略指定属性的转换
当在进行转换时,需要忽略指定属性的转换,可以通过 @AutoMapping
的 ignore
来配置。
Car
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
private String name;
@AutoMapping(ignore = true)
private String type;
private SeatConfiguration seatConfiguration;
}
2
3
4
5
6
7
8
9
10
11
CarDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
private String name;
private String type;
private SeatConfiguration seatConfiguration;
}
2
3
4
5
6
7
8
9
测试
@SpringBootTest
public class IgnoreConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
SeatConfiguration seatConfiguration = new SeatConfiguration().setName("真皮座椅").setPrice(5000);
Car car = new Car().setName("宝马").setType("小轿车").setSeatConfiguration(seatConfiguration);
CarDto carDto = this.converter.convert(car, CarDto.class);
System.out.println(carDto); // CarDto(name=宝马, type=null, seatConfiguration=SeatConfiguration(name=真皮座椅, price=5000))
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 属性转换时的默认值
@AutoMapping
中的 defaultValue
可以指定在转换属性时,当属性为 null
时,转换到目标类中的默认值。
DefaultDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = DefaultVo.class)
public class DefaultDto {
@AutoMapping(defaultValue = "18")
private Integer i;
@AutoMapping(defaultValue = "1.32")
private Double d;
@AutoMapping(defaultValue = "true")
private Boolean b;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DefaultVo
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DefaultVo {
private Integer i;
private Double d;
private Boolean b;
}
2
3
4
5
6
7
8
9
10
11
12
13
测试
@SpringBootTest
public class DefaultValueConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
DefaultDto defaultDto = new DefaultDto();
DefaultVo defaultVo = this.converter.convert(defaultDto, DefaultVo.class);
System.out.println(defaultVo); // DefaultVo(i=18, d=1.32, b=true)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 表达式
在执行属性转换时,可以通过指定执行一段 Java 代码来进行转换操作,例如,对源对象中的某个属性进行转换后返回。
需要注意的是,在生成时,会直接将表达式插入到转换逻辑中,并不会验证其语法。
例如,将源对象中的 List<String>
属性,通过 ,
拼接为字符串:
User
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = UserDto.class)
public class User {
private String username;
private int age;
private boolean young;
@AutoMapping(target = "educations", expression = "java(java.lang.String.join(\",\", source.getEducationList()))")
private List<String> educationList;
}
2
3
4
5
6
7
8
9
10
11
12
UserDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class UserDto {
private String username;
private int age;
private boolean young;
private String educations;
}
2
3
4
5
6
7
8
9
10
测试
@SpringBootTest
public class ExpressionConvertTest {
private Converter converter = new Converter();
@Test
public void test() {
User user = new User().setUsername("张三").setAge(23).setYoung(true).setEducationList(Arrays.asList("1", "2", "3"));
UserDto userDto = this.converter.convert(user, UserDto.class);
System.out.println(userDto); // UserDto(username=张三, age=23, young=true, educations=1,2,3)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 自定义类型转换器
当不同类型的属性,想要按照自定义的规则进行转换时,可以有两种办法:
- 通过
@AutoMapping
中配置的expression
表达式配置 - 自定义一个类型转换器,通过
@AutoMapper
的uses
属性来引入
方式一可以上面面的 表达式。
这里基于方式二,实现将 String
类型的属性,根据逗号分隔,转换为 List<String>
类型的属性:
首先,定义一个类型转换器:StringToListStringConverter
StringToListStringConverter
@Component
public class StringToListStringConverter {
public static List<String> stringToListString(String str) {
return StrUtil.split(str);
}
}
2
3
4
5
6
User
@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = UserDto.class)
public class User {
private String username;
private int age;
private boolean young;
private List<String> educationList;
}
2
3
4
5
6
7
8
9
10
UserDto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = User.class, uses = StringToListStringConverter.class)
public class UserDto {
private String username;
private int age;
private boolean young;
@AutoMapping(target = "educationList")
private String educations;
}
2
3
4
5
6
7
8
9
10
11
12
测试
@SpringBootTest
public class CustomerConverterTest {
private Converter converter = new Converter();
@Test
public void test() {
UserDto userDto = new UserDto().setAge(23).setUsername("张三").setYoung(true).setEducations("1,2,3");
User user = converter.convert(userDto, User.class);
System.out.println(user); // User(username=张三, age=23, young=true, educationList=[1, 2, 3])
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自动接入自定义转换接口
当有的类型转换逻辑比较复杂,可以通过自定义转换接口来实现,即使用 MapStruct 原生的方式。
当使用这种方式时,默认生成的类型转换中,如果有前面提供的类型转换时,会自动引用。
Car
@AutoMapper(target = CarDto.class)
@Data
public class Car {
private Tyre tyre;
}
2
3
4
5
CarDto
@Data
public class CarDto {
private TyreDTO tyre;
}
2
3
4
这里定义 Tyre
和 TyreDTO
之间的转换接口
TyreMapper
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface TyreMapper {
TyreDTO tyreToTyreDTO(Tyre tyre);
Tyre tyreDtoToTyre(TyreDTO tyreDTO);
}
2
3
4
5
6
7
8
# 反向属性映射配置
前面前面提到,当在一个类上面添加 @AutoMapper
注解时,默认情况下,除了会生成源类到目标类的转换接口,还会生成目标类到源类的转换接口和实现类,这里需要注意的是,默认情况下生成的该转换接口,并没有任何自定义配置,即使在源类中配置了 @AutoMapping
注解。
这里要实现目标类到源类的自定义转换配置,可以有两种方式:
- 在目标类上面添加
@AutoMapper
注解。这是最建议的方式,当转换双方都有添加该注解时,便不会生成默认的转换接口,即按照自定义的规则进行生成 - 当目标类访问不到源类,或者项目规范不允许在目标类上面添加该种注解时,可以将自定义配置全部添加在源类中。这就是下面要介绍的 反向属性映射配置
框架中提供了 @ReverseAutoMapping
注解,该注解就是为了配置目标类到源类的自定义转换规则。
注意
这里需要注意的是,防止配置冲突,一旦添加 @ReverseAutoMapping
注解,在目标类中,便不能添加任何自定义转换注解。
@ReverseAutoMapping
注解表示的含义,是目标类到源类转换时,需要指定的自定义转换规则,其中可以配置的属性,与 @AutoMapping
注解一致。
这里有两个属性需要注意,分别是 source
和 target
。
这里的 source
指的是目标类中的属性,target
指的是源类中的属性。
能会有人这里有疑问,为什么这里的配置像是反的?如果没有,可以直接跳过。
框架设计的时候,所有的属性转换配置,都是基于要转换的类型,该类转换为目标类,想要应用的效果。这里的 source
也应该是来源类中的属性。
如果还是不理解,这里可以认为,该注解就是本该应用在目标类中的 @AutoMapping
注解,原封不动拷贝到当前类,再修改注解名称即可。
Student 只在源类配置正向和反向转换规则
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = StudentDto.class)
public class Student {
@AutoMapping(source = "name", target = "sname")
@ReverseAutoMapping(source = "sname", target = "name")
private String name;
@AutoMapping(source = "age", target = "sage")
@ReverseAutoMapping(source = "sage", target = "age")
private Integer age;
}
2
3
4
5
6
7
8
9
10
11
12
13
StudentDto 在目标类并未配置转换规则
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class StudentDto {
private String sname;
private Integer sage;
}
2
3
4
5
6
7
8
测试
@SpringBootTest
public class ReverseAutoMappingConvertTest {
@Resource
private Converter converter;
@Test
public void test() {
Student student = new Student().setName("张三").setAge(23);
StudentDto studentDto = this.converter.convert(student, StudentDto.class);
System.out.println(studentDto); // StudentDto(sname=张三, sage=23)
StudentDto studentDto1 = new StudentDto().setSname("李四").setSage(18);
Student student1 = this.converter.convert(studentDto1, Student.class);
System.out.println(student1); // Student(name=李四, age=18)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18