MapStruct简化JavaBean的转换

MapStruct是一个注解处理器,用于简化JavaBean之间的转换,生成类型安全且高效的映射代码。相比于Spring的BeanUtils,MapStruct在编译时生成代码,提供更好的性能和扩展性,尤其适合高并发场景。使用MapStruct可以减少手动转换代码,提高开发效率。文章介绍了MapStruct的使用方法,包括引入依赖、定义映射接口和实现转换。

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,它俩有以下区别:

  1. 编译时生成代码 vs 运行时反射:MapStruct生成的映射代码是在编译时生成的,而BeanUtils则是在运行时使用反射机制实现转换。

  2. 性能和可扩展性:由于MapStruct生成的代码是类型安全的,因此可以比使用反射更加高效和可靠。同时,MapStruct还能够自定义转换逻辑并支持扩展,使得它更加灵活和可扩展。

  3. 集成方式:MapStruct可以无缝集成到Spring中,也可以与其他IoC容器结合使用;而BeanUtils是Spring框架自带的工具类。

  4. 映射规则的定义方式: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;
    }
}

Comment