MapStruct的原理与使用

1、MapStruct是什么

MapStruct是一个Java注释处理器(annotation processor),用于自动生成类型安全的Java Bean映射器,它可以轻松地将一个Java Bean类型的数据转换为另一个Java Bean类型的数据。

在Java应用程序中,我们通常需要将一个对象转换为另一个对象。这个过程需要手动编写代码,需要大量的时间和精力,而且容易出错。MapStruct的目标就是简化这个过程,通过注释处理器来自动生成转换代码,从而节省开发时间和减少错误。

MapStruct支持注释处理器,可以在编译时生成类型安全的转换代码。它使用注释来指定数据映射的细节和规则,然后自动生成对应的Java Bean映射器代码。MapStruct生成的代码是类型安全的,因为它使用了Java编译器的类型检查机制。

使用MapStruct可以大大提高代码的可读性和可维护性,同时也可以提高应用程序的性能。MapStruct是一个开源项目,可以在GitHub上找到它的源代码和文档。

2、MapStruct与BeanUtils有什么区别

MapStruct和BeanUtils都是用于Java Bean之间的转换,但它们有以下几个区别:

1、MapStruct是基于注解处理器的代码生成器,它在编译时生成类型安全的转换代码。而BeanUtils是运行时使用反射进行转换,没有生成代码的过程,因此性能较低。
2、MapStruct的生成代码是类型安全的,不会出现编译时错误,而BeanUtils则需要在运行时才能发现错误。
3、MapStruct可以通过注解控制数据映射的方式,包括字段名称、字段类型、格式转换等,而BeanUtils只能复制相同名称的属性。
4、MapStruct的注解方式比BeanUtils更加直观,易于理解和维护。
5、MapStruct支持复杂类型转换,例如集合和嵌套对象之间的转换,而BeanUtils只支持简单类型转换。

综上所述,MapStruct相对于BeanUtils有更好的性能、类型安全和可维护性,而且支持复杂类型转换,因此在Java Bean之间的转换中更加适合使用。

3、怎么使用MapStruct

首先,我们来看一个需求:
业务层处理了一个需求,并把处理结果存在CarDto中,现在要把它转化为CarVo传给前端,请问怎么把CarDto快速转化为CarVo?

整体结构

在这里插入图片描述

Dto

CarDto类

/**
 * 车辆Dto
 */
@Data
public class CarDto {
    /**
     * 车辆id
     */
    private int id;
    /**
     * 车辆名字
     */
    private String name;
    /**
     * 车辆价格
     */
    private double price;
    /**
     * 车辆数量
     */
    private int num;
    /**
     * 车辆部件
     */
    private List<PartDto> partList;
    /**
     * 驾驶人
     */
    private PersonDto personDto;
}

PartDto类

/**
 * 车辆零部件Dto
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PartDto {
    /**
     * 零部件id
     */
    private int partId;
    /**
     * 零部件名字
     * 
     */
    private String partName;
}

PersonDto类

/**
 * 驾驶人Dto
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PersonDto {
    /**
     * 驾驶人id
     */
    private int personId;
    /**
     * 驾驶人名字
     */
    private String  personName;
}

Vo

CarVo类

 * 车辆Vo
 */
@Data
public class CarVo {
    /**
     * 车辆id
     */
    private Integer id;
    /**
     * 车辆名字
     */
    private String name;
    /**
     * 车辆价格
     */
    private String price;
    /**
     * 车辆数量
     */
    private Integer carNum;
    /**
     * 车辆是否存在零部件
     */
    private Boolean isPart;
    /**
     * 驾驶人
     */
    private PersonVo personVo;
}

PersonVo类

/**
 * 驾驶人Vo
 */
@Data
public class PersonVo {
    /**
     * 驾驶人id
     */
    private Integer id;
    /**
     * 驾驶人名字
     */
    private String name;
}

传统方法

对于这个需求,我们通常想到的方法是把CarDto中的属性一个一个赋值给CarVo

public class Test1 {
    public static void main(String[] args) {
        CarDto carDto = getCarDto();

        CarVo carVo = new CarVo();
        // 批量把CarDto赋值给CarVo
        carVo.setId(carDto.getId());
        carVo.setName(carDto.getName());
        carVo.setPrice(String.valueOf(carDto.getPrice()));
        carVo.setCarNum(carDto.getNum());

        // 判断CarDto的零部件是否有值
        List<PartDto> dtoPartList = carDto.getPartList();
        boolean isPart=(dtoPartList!=null&&dtoPartList.size()!=0);
        carVo.setIsPart(isPart);

        // 把CartDto的驾驶人赋值给CarVO
        PersonDto personDto = carDto.getPersonDto();
        PersonVo personVo = new PersonVo();
        personVo.setId(personDto.getPersonId());
        personVo.setName(personDto.getPersonName());
        carVo.setPersonVo(personVo);

        // 输出
        System.out.println(carDto);
        System.out.println(carVo);
    }

    // 获得对应的CarDto
    private static CarDto getCarDto(){
        CarDto carDto = new CarDto();
        carDto.setId(1);
        carDto.setName("丰田");
        carDto.setPrice(4.20);
        carDto.setNum(50);
        carDto.setPersonDto(new PersonDto(10,"张三"));

        ArrayList<PartDto> partDtoList = new ArrayList<>();
        partDtoList.add(new PartDto(3,"轮胎"));
        carDto.setPartList(partDtoList);
        return carDto;
    }
}

在这里插入图片描述

可以看到成功赋值。但是这种代码太多,如果写在业务层不能突出业务逻辑的重点,那么有没有一种快速的赋值方法呢?我们可以使用MapStruct进行快速赋值

使用MapStruct

首先,需要导入MapStruct的jar包

		<dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Final</version>
        </dependency>

其次,编写CarConvert转换接口,本接口中所使用到的注解都是org.mapstruct包下的

@Mapper
public interface CarConvert {
    CarConvert INSTANCE=Mappers.getMapper(CarConvert.class);

    /**
     * Mapping注解的参数解释:
     *      1、source:源属性名称,即CarDto类下的属性名称
     *      2、target:目标属性名称,即CarVo类下的属性名称
     */
    @Mappings({
            @Mapping(source = "num",target = "carNum"),
            @Mapping(source = "personDto",target = "personVo")
    })
    public CarVo getCarVo(CarDto carDto);

    /**
     * 指定PersonDto转换为PersonVo
     */
    @Mappings({
            @Mapping(source = "personId",target = "id"),
            @Mapping(source = "personName",target = "name")
    })
    public PersonVo personDtoToPersonVo(PersonDto personDto);

    /**
     * 在映射的最后一步对属性的自定义映射处理
     */
    @AfterMapping //表示让MapStruct在调用完自动转换方法后,回来自动调用本方法
    public default void dtoVoAfter(CarDto carDto,@MappingTarget CarVo carVo){
        // @MappingTarget : 表示传来的carVO对象是已经赋过值的

        // 判断CarDto的零部件是否有值
        List<PartDto> dtoPartList = carDto.getPartList();
        boolean isPart=(dtoPartList!=null&&dtoPartList.size()!=0);
        carVo.setIsPart(isPart);
    }
}

编写测试类

public class Test2 {
    public static void main(String[] args) {
        CarDto carDto = getCarDto();

        // 根据你配置的映射规则把CarDto转化为CarVO
        CarVo carVo = CarConvert.INSTANCE.getCarVo(carDto);

        // 输出
        System.out.println(carDto);
        System.out.println(carVo);
    }

    // 获得对应的CarDto
    private static CarDto getCarDto(){
        CarDto carDto = new CarDto();
        carDto.setId(1);
        carDto.setName("丰田");
        carDto.setPrice(4.20);
        carDto.setPersonDto(new PersonDto(10,"张三"));
        carDto.setNum(50);

        ArrayList<PartDto> partDtoList = new ArrayList<>();
        partDtoList.add(new PartDto(3,"轮胎"));

        carDto.setPartList(partDtoList);
        return carDto;
    }
}

在这里插入图片描述
可以看到同样转换成功

4、总结与原理

@Mapper默认映射规则

1、同类型且同名的属性,会自动映射
2、MapStruct会自动进行类型转换
① 8种基本类型和太慢对应的包装类型之间
② 8种基本类型(包括它们的包装类型)和String之间
③ 日期类型和String之间
3、source或target多余的属性对方没有,不会报错
4、属性是应用对象的映射处理:多加一个接口方法进行处理

@AfterMapping和@MappingTarget

在映射最后一步对属性的自定义映射处理

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值