MapStruct高级拷贝工具的使用
在MapStruct中,可以通过多种方式实现复杂的自定义映射和嵌套对象的映射。MapStruct提供了强大的注释和配置功能来处理这些复杂场景。
如何使用 MapStruct
1、引入依赖
在pom.xml中添加MapStruct依赖(maven版)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency> </dependencies>
|
2、定义Mapper接口
需要定义一个Mapper接口,MapStruct会在编译时生成实现类。
假设现在有两个类Source和TargetDto,可以创建如下Mapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers;
@Mapper public interface SourceToDtoMapper {
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
TargetDTO convertToDto(Source source); }
|
3、调用Mapper
在代码中调用这个Mapper来完成对象转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import org.springframework.cglib.core.ReflectUtils;
public static <S, T> T convertObjectToDto(S source, Class<T> dtoClass) { if (source == null || dtoClass == null) { throw new IllegalArgumentException("数据源对象或DTO类类型不能为空"); } try { Constructor<T> constructor = dtoClass.getDeclaredConstructor(); constructor.setAccessible(true); T dto = constructor.newInstance(); BeanUtils.copyProperties(source, dto); return dto; } catch (Exception e) { throw new RuntimeException("无法将数据源对象转换为DTO对象", e); } }
|
总结
- 高性能:
MapStruct在编译时生成代码,避免了运行时反射操作的性能开销
- 简化嵌套对象映射:通过
MapStruct自动处理嵌套对象的映射,避免手动递归
- 可维护性:使用
MapStruct接口定义映射规则,代码更直观可读,维护性更高。
高级用法
自定义字段映射
如果源对象和目标对象的字段名称不同,或者需要对某些字段进行特定的转换,可以使用**@Mappering**注解来自定义映射
示例
假设有两个类 Source 和 TargetDTO,其中 Source 的 name 字段需要映射到 TargetDTO 的 fullName 字段,birthDate 字段需要转换为 age。
1 2 3 4 5 6 7 8 9 10 11 12
| public class Source { private String name; private LocalDate birthDate; }
public class TargetDTO { private String fullName; private int age; }
|
通过 @Mapping 注解自定义字段映射,使用自定义方法进行 birthDate 到 age 的转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers;
import java.time.LocalDate; import java.time.Period;
@Mapper public interface SourceToDtoMapper {
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
@Mapping(source = "name", target = "fullName") @Mapping(source = "birthDate", target = "age", qualifiedByName = "calculateAge") TargetDTO convertToDto(Source source);
default int calculateAge(LocalDate birthDate) { return Period.between(birthDate, LocalDate.now()).getYears(); } }
|
嵌套对象映射
当源对象和目标对象中包含嵌套对象时,MapStruct可以自动处理这些嵌套对象的映射,或者可以显示指定如何处理嵌套的映射。
示例
假设 Source 和 TargetDTO 都包含嵌套对象 Address,你希望将 Source 的嵌套对象也映射到 TargetDTO 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Source { private String name; private Address address; }
public class Address { private String street; private String city; }
public class TargetDTO { private String fullName; private AddressDTO address; }
public class AddressDTO { private String street; private String city; }
|
MapStruct 会自动识别并映射嵌套对象。如果需要,可以单独定义一个映射方法来处理嵌套对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Mapper public interface SourceToDtoMapper {
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
@Mapping(source = "name", target = "fullName") TargetDTO convertToDto(Source source);
AddressDTO addressToAddressDTO(Address address); }
|
调用
1 2 3 4 5 6 7 8 9 10 11 12 13
| Address address = new Address(); address.setStreet("Main Street"); address.setCity("Springfield");
Source source = new Source(); source.setName("Tom"); source.setAddress(address);
TargetDTO dto = SourceToDtoMapper.INSTANCE.convertToDto(source); System.out.println(dto.getAddress().getStreet()); System.out.println(dto.getAddress().getCity());
|
集合映射
MapStruct还支持集合类型(如 List,Set)的映射。它会自动将源代码中的集合映射到目标对象中的集合
示例
假设 Source 和 TargetDTO 包含一个 List<Address>,MapStruct 会自动映射这些集合中的每一个元素。
1 2 3 4 5 6 7 8 9 10
| public class Source { private List<Address> addresses; }
public class TargetDTO { private List<AddressDTO> addresses; }
|
无需额外配置,MapStruct 会自动处理 List<Address> 到 List<AddressDTO> 的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Mapper public interface SourceToDtoMapper {
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
@Mapping(source = "name", target = "fullName") TargetDTO convertToDto(Source source);
AddressDTO addressToAddressDTO(Address address); }
|
嵌套字段的自定义映射
可以通过 @Mapping 注解映射嵌套对象的特定字段。
示例
假设 Source 中的 Address 对象的 city 字段需要映射到 TargetDTO 中的 location 字段。
1 2 3 4 5 6
| public class TargetDTO { private String fullName; private String location; }
|
1 2 3 4 5 6 7 8 9 10
| @Mapper public interface SourceToDtoMapper {
SourceToDtoMapper INSTANCE = Mappers.getMapper(SourceToDtoMapper.class);
@Mapping(source = "name", target = "fullName") @Mapping(source = "address.city", target = "location") TargetDTO convertToDto(Source source); }
|
调用
1 2 3 4 5 6 7 8 9
| Source source = new Source(); source.setName("Tom"); Address address = new Address(); address.setCity("Springfield"); source.setAddress(address);
TargetDTO dto = SourceToDtoMapper.INSTANCE.convertToDto(source); System.out.println(dto.getLocation());
|
多个源对象映射到一个目标对象
MapStruct 支持将多个源对象映射到一个目标对象,使用 @Mapping 注解可以指定来自不同源对象的字段。
示例
假设你有两个源对象 Person 和 Address,你希望将它们的属性合并到 PersonDTO 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Person { private String firstName; private String lastName; }
public class Address { private String city; private String street; }
public class PersonDTO { private String fullName; private String city; }
|
Mapper中通过 @Mapping 注解指定字段的来源,并通过参数顺序来处理多个源对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Mapper public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "person.firstName", target = "fullName", qualifiedByName = "concatNames") @Mapping(source = "address.city", target = "city") PersonDTO toPersonDto(Person person, Address address);
default String concatNames(String firstName, String lastName) { return firstName + " " + lastName; } }
|
调用
1 2 3 4 5 6 7 8 9 10 11
| Person person = new Person(); person.setFirstName("John"); person.setLastName("Doe");
Address address = new Address(); address.setCity("New York");
PersonDTO dto = PersonMapper.INSTANCE.toPersonDto(person, address); System.out.println(dto.getFullName()); System.out.println(dto.getCity());
|
总结:
- 自定义映射:使用
@Mapping 注解可以轻松自定义字段映射,支持字段名不同、字段类型不同的场景。
- 嵌套对象映射:MapStruct 支持嵌套对象自动映射,也支持自定义映射规则。
- 集合映射:MapStruct 能自动处理集合的映射,确保每个元素被正确映射。
- 多个源对象映射:可以轻松将多个源对象合并到一个目标对象。