1.前言
大多数时候,终端用户或服务不需要访问模型中的全部数据,而只需要访问某些特定的部分。DTO经常被用于这些应用中。DTO只是持有另一个对象中被请求的信息。通常情况下,这些信息是有限的一部分。例如,在持久化层定义的实体和发往客户端的DTO之间经常会出现相互之间的转换。由于DTO是原始对象的反映,因此这些类之间的映射器在转换过程中扮演着关键角色。这就是MapStruct解决的问题:手动创建bean映射器非常耗时。 但是该库可以自动生成Bean映射器类。
MapStruct通过使用注解,在源代码中指定映射规则,MapStruct可以自动生成转换器代码。MapStruct支持各种转换场景,包括简单类型、集合、继承、日期、枚举、嵌套映射等等。同时,它还能够与Spring和CDI等IoC容器无缝集成,方便地将MapStruct转换器注入到应用程序中。
更多内容请浏览官网:MapStruct – Java bean mappings, the easy way!
通常我们在开发中比较常用的实现JavaBean之间的转换应该就是org.springframework.beans.BeanUtils,它俩有以下区别:
编译时生成代码 vs 运行时反射:MapStruct生成的映射代码是在编译时生成的,而BeanUtils则是在运行时使用反射机制实现转换。
性能和可扩展性:由于MapStruct生成的代码是类型安全的,因此可以比使用反射更加高效和可靠。同时,MapStruct还能够自定义转换逻辑并支持扩展,使得它更加灵活和可扩展。
集成方式:MapStruct可以无缝集成到Spring中,也可以与其他IoC容器结合使用;而BeanUtils是Spring框架自带的工具类。
映射规则的定义方式:MapStruct使用基于注解的方式在源代码中定义映射规则,而BeanUtils则需要手动编写复杂的转换方法。
2.为何要选择MapStruct
高效
在一些高并发的场景,性能是非常重要的,BeanUtils虽然也可以方便地完成JavaBean之间的转换,但是由于其底层是基于反射实现的,在高并发场景下难免会出现大规模的数据处理和转换操作,这时候还是用BeanUtils会导致接口响应速度有所下降。
这时候,最高效的方法就是原生的去手动get/set,但是这种需要反复写大量重复的转换代码,并且这些代码难以被反复利用,于是就考虑使用MapStruct。
使用简单
我们只需要声明一个接口,接口下写对应的方法,就可以使用了。当然,如果有特殊情况,是需要额外处理的。
代码独立、非入侵性
不需要在原有的类(PO、DTO)中增加任何注解,只需要在转换时调用既可实现转换功能(与lombok相比)
调试方便
代码编译后,我们可以直接进入生成impl类,进行查看、debug
3.使用方法
3.1导入依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
3.2结合SpringBoot使用
@Mapper(componentModel = "spring")
public interface MyEntityMapper {
MyDto entityToDto(MyEntity entity);
@Mapping(target = "createdDate", expression = "java(java.time.LocalDate.now())")
MyDto entityToDtoWithDefaultDate(MyEntity entity);
}
在Spring组件中,通过自动装配MapStruct Mapper接口来实现对象之间的自动映射。
@Service
public class MyService {
private final MyEntityMapper myEntityMapper;
@Autowired
public MyService(MyEntityMapper myEntityMapper) {
this.myEntityMapper = myEntityMapper;
}
public MyDto convert(MyEntity entity) {
return myEntityMapper.entityToDto(entity);
}
}
确保Spring Boot的启动类或配置类中包含了@EnableAutoConfiguration
或@SpringBootApplication
注解,这样Spring Boot就能自动配置应用程序,包括MapStruct的Mapper组件。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3.3实际案例
构建Dto与Bo转换接口
@Mapper
public interface ExampleConverter {
ExampleConverter INSTANCE = Mappers.getMapper(ExampleConverter.class);
ExampleDto toExampleDto(ExampleBo bo);
ExampleBo toExampleBo(ExampleDto dto);
List<ExampleBo> toExampleBoList(List<ExampleDto> dtoList);
List<ExampleDto> toExampleDtoList(List<ExampleBo> boList);
}
在服务实现类中使用转换
@Service
@Transactional(rollbackFor = Exception.class)
public class ExampleServiceImpl extends ServiceImpl<ExampleMapper, ExampleBo> {
public void save(String exampleId, ExampleDto exampleDto) {
ExampleBo exampleBo =
ExampleConverter.INSTANCE.toExampleBo(exampleDto);
exampleBo.setExampleId(exampleId);
this.baseMapper.insert(exampleBo);
}
}
编译后自动生成的代码部分
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
public class ExampleConverterImpl implements ExampleConverter {
@Override
public ExampleDto toExampleNamingClassificationDto(ExampleBo bo) {
if ( bo == null ) {
return null;
}
ExampleDto ExampleDto = new ExampleDto();
ExampleDto.setExampleId( bo.getExampleId() );
ExampleDto.setExampleName( bo.getExampleName() );
ExampleDto.setCreateQuantity( bo.getCreateQuantity() );
return ExampleDto;
}
@Override
public ExampleBo toExampleBo(ExampleDto dto) {
if ( dto == null ) {
return null;
}
ExampleBo ExampleBo = new ExampleBo();
ExampleBo.setExampleId( dto.getExampleId() );
ExampleBo.setExampleName( dto.getExampleName() );
ExampleBo.setCreateQuantity( dto.getCreateQuantity() );
return ExampleBo;
}
@Override
public List<ExampleBo> toExampleBoList(List<ExampleDto> dtoList) {
if ( dtoList == null ) {
return null;
}
List<ExampleBo> list = new ArrayList<ExampleBo>( dtoList.size() );
for ( ExampleDto ExampleDto : dtoList ) {
list.add( toExampleBo( ExampleDto ) );
}
return list;
}
@Override
public List<ExampleDto> toExampleDtoList(List<ExampleBo> boList) {
if ( boList == null ) {
return null;
}
List<ExampleDto> list = new ArrayList<ExampleDto>( boList.size() );
for ( ExampleBo ExampleBo : boList ) {
list.add( toExampleDto( ExampleBo ) );
}
return list;
}
}