MapStruct的使用
- 1、MapStruct是什么
- 2、MapStruct与BeanUtils有什么区别
- 3、怎么使用MapStruct
- 整体结构
- Dto
- CarDto类
- PartDto类
- PersonDto类
- Vo
- CarVo类
- PersonVo类
- 传统方法
- 使用MapStruct
- 4、总结与原理
- @Mapper默认映射规则
- @AfterMapping和@MappingTarget
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
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
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 partDtoList = new ArrayList<>(); partDtoList.add(new PartDto(3,"轮胎")); carDto.setPartList(partDtoList); return carDto; } } 可以看到成功赋值。但是这种代码太多,如果写在业务层不能突出业务逻辑的重点,那么有没有一种快速的赋值方法呢?我们可以使用MapStruct进行快速赋值
使用MapStruct
首先,需要导入MapStruct的jar包
org.mapstruct mapstruct 1.2.0.Final org.mapstruct mapstruct-processor 1.2.0.Final 其次,编写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
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
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
在映射最后一步对属性的自定义映射处理