1 MapStruct配置
MapStuct的使用非常简单,把对应的jar包引入即可。
1.3.1.Final
org.mapstruct
mapstruct
${mapstruct.version}
org.mapstruct
mapstruct-processor
${mapstruct.version}
2 原理&性能
2.1 实现原理
对象拷贝工具实现上一般分为2种:
(1) 在运行时,通过反射调用set/get方法或者直接对成员变量进行赋值。
(2)在编译期,生成调用get/set方法进行赋值的代码,生成对应的class文件。
MapStrut属于第二种,在编译期间消耗少许的时间,换取运行时的高性能。
接口声明:
@Mapper
public interfaceProductAssembler {
SkuDTO toDTO(Sku sku);
}
编辑生成的class反编译
public class ProductAssemblerImpl implementsProductAssembler {
@Override
publicSkuDTO toDTO(Sku sku) {
if ( sku == null) {
return null;
}
SkuDTO skuDTO = newSkuDTO();
skuDTO.setSkuId( sku.getSkuId() );
returnskuDTO;
}
}
2.2 性能对比
3 使用方法
使用@Mapper注解,声明映射器,可以是接口,或者抽象类。
使用@Mapping注解,实现灵活的字段映射,定制映射的规则。
3.1 转换器的检索
在声明好转换接口之后,MapStruct提供几种方式获取生成的Mapper映射器。
3.1.1 使用Mappers工厂获取
可以通过提供的Mappers工厂类,获取指定的类型。
@Mapper
public interfaceAssembler {
//使用工厂方法获取Mapper实例
Assembler INSTANCE = Mappers.getMapper(Assembler.class);
ProductDTO toDTO(Product product);
}
3.1.2 通过依赖注入的方式获取
MapStuct同时支持和其他框架结合,通过依赖注入的方式获取Mapper实例。目前支持spring和cdi。
@Mapper(componentModel = "spring")
public interfaceAssembler {
ProductDTO toDTO(Product product);
}
@Component
public class AssemblerImpl implementsAssembler {
@Override
publicProductDTO toDTO(Product product) {
if ( product == null) {
return null;
}
ProductDTO productDTO = newProductDTO();
productDTO.setProductId( product.getProductId() );
returnproductDTO;
}
}
3.2 简单映射
3.2.1 基本映射
对于同名同属性的字段,无需特别声明指定,自动转换。
对于不同名相同属性的字段,可以使用Mapping注解指定。
@Data
@NoArgsConstructor
@AllArgsConstructorpublic classProduct {privateString productId;privateString name;
}
Product
@Data
@NoArgsConstructor
@AllArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = -6780322740093464581L;privateString productId;privateString productName;
}
ProductDTO
定义映射器:
@Mapper(componentModel = "spring")
public interfaceAssembler {
@Mapping(source = "name", target = "productName")
ProductDTO toDTO(Product product);
}
生成的映射器试实现:
@Component
public class AssemblerImpl implementsAssembler {
@Override
publicProductDTO toDTO(Product product) {
if ( product == null) {
return null;
}
ProductDTO productDTO = newProductDTO();
productDTO.setProductName( product.getName() ); //不同字段名映射
productDTO.setProductId( product.getProductId() ); //相同映射名自动转换
returnproductDTO;
}
}
3.2.2 多源参数映射
支持把多个参数映射成一个类型,使用@Mapping指定即可。
@Mapper(componentModel = "spring")public interfaceDemo6Assembler {
@Mapping(target= "productId", source = "product.productId")
@Mapping(target= "desc", source = "detail.desc")
ProductDTO toDetailDTO(Product product, ProductDetail detail);
}
3.2.3 更新对象
映射时除了生成新的新对象外,还支持现存对象的更新:
@Mapper(componentModel = "spring")public interfaceDemo6Assembler {
@Mapping(target= "desc", source = "desc")voidupdateDTO(@MappingTarget ProductDTO productDTO, ProductDetail detail);
}
3.3 数据类型转换
3.3.1 对于基础数据类型会进行自动隐式的转换
如int、long、String,Integer、Long等。
@Data
@NoArgsConstructor
@AllArgsConstructorpublic classProduct {privateString productId;privateLong price;
}
Product
@Data
@NoArgsConstructor
@AllArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = -6780322740093464581L;private intproductId;privateString price;
}
ProductDTO
定义映射器:
@Mapper(componentModel = "spring")
public interfaceAssembler {
ProductDTO toDTO(Product product);
}
生成的映射代码:
@Component
public class AssemblerImpl implementsAssembler {
@Override
publicProductDTO toDTO(Product product) {
if ( product == null) {
return null;
}
ProductDTO productDTO = newProductDTO();
if ( product.getProductId() != null) {
//String自动转int
productDTO.setProductId( Integer.parseInt( product.getProductId() ) );
}
if ( product.getPrice() != null) {
//Long转String
productDTO.setPrice( String.valueOf( product.getPrice() ) );
}
returnproductDTO;
}
}
3.3.2 指定转换格式
某些类型的转换,我们可以指定具体转换的格式。
(1)对于基本数据类型与String之间的转换,可以使用 numberFormat 指定转换格式,使用的是java.text.DecimalFormat 实现。
@Data
@NoArgsConstructor
@AllArgsConstructorpublic classProduct {privateString productId;privateBigDecimal price;privateString stock;
}
Product
@Data
@NoArgsConstructor
@AllArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = -6780322740093464581L;privateString productId;privateString price;privateInteger stock;
}
ProductDTO
映射器定义:
@Mapper(componentModel = "spring")public interfaceDemo3Assembler {
@Mapping(target= "price", numberFormat = "#.00元") //BigDecimal转换成字符串
@Mapping(target= "stock", numberFormat = "#个") //字符串转换成int
ProductDTO toDTO(Product product);
}
实现代码:
@Componentpublic class Demo3AssemblerImpl implementsDemo3Assembler {
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );if ( product.getPrice() != null) {//BigDecimal格式化成字符串
productDTO.setPrice( createDecimalFormat( "#.00元").format( product.getPrice() ) );
}try{if ( product.getStock() != null) {//字符串格式化为int
productDTO.setStock( new DecimalFormat( "#个").parse( product.getStock() ).intValue() );
}
}catch( ParseException e ) {throw newRuntimeException( e );
}returnproductDTO;
}privateDecimalFormat createDecimalFormat( String numberFormat ) {
DecimalFormat df= newDecimalFormat( numberFormat );
df.setParseBigDecimal(true);returndf;
}
}
测试代码:
@Testpublic voidtest2() {
com.gotten.study.mapstruct.demo3.Product product= newcom.gotten.study.mapstruct.demo3.Product ();
product.setProductId("P001");
product.setPrice(new BigDecimal("100"));
product.setStock("1个");
com.gotten.study.mapstruct.demo3.ProductDTO productDTO=demo3Assembler.toDTO(product);
System.out.println("productDTO:" +JSON.toJSONString(productDTO));
}
productDTO:{"price":"100.00元","productId":"P001","stock":1}
test
(2)Date和String之间的转换,可以通过dateFormat指定转换格式,使用的是SimpleDateFormat的实现。
@Data
@NoArgsConstructor
@AllArgsConstructorpublic classProduct {privateString productId;privateDate saleTime;privateString validTime;
}
Product
@Data
@NoArgsConstructor
@AllArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = -6780322740093464581L;privateString productId;privateString saleTime;privateDate validTime;
}
ProductDTO
定义映射器:
@Mapper(componentModel = "spring")public interfaceDemo4Assembler {
@Mapping(target= "saleTime", dateFormat = "yyyy-MM-dd HH:mm:ss") //Date转换成String
@Mapping(target= "validTime", dateFormat = "yyyy-MM-dd HH:mm") //String转换成Date
ProductDTO toDTO(Product product);
}
实现代码:
@Componentpublic class Demo4AssemblerImpl implementsDemo4Assembler {
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );if ( product.getSaleTime() != null) {
productDTO.setSaleTime(new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( product.getSaleTime() ) ); //转换成String
}try{if ( product.getValidTime() != null) {
productDTO.setValidTime(new SimpleDateFormat( "yyyy-MM-dd HH:mm" ).parse( product.getValidTime() ) ); //转换成Date
}
}catch( ParseException e ) {throw newRuntimeException( e );
}returnproductDTO;
}
}
3.3.3 属性为复杂对象的映射
(1)如果是相同类型的对象引用,不会创建新的对象,直接把对象的引用从源对象赋值给目标对象。
(2)如果类型相同,但是是集合类的引用,会创建一个新的集合,集合里面的所有引用进行拷贝。
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );
List list =product.getSkuList();if ( list != null) {
productDTO.setSkuList(new ArrayList( list ) ); //创建新的集合,并对所有元素进行拷贝
}returnproductDTO;
}
(3)对象的类型不同,会检查映射器中是否存在对应的映射方法,如果存在,直接使用,否则会尝试自动创建子映射方法。
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProduct {privateString productId;privateProductDetail productDetail;
}
Product
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProductDetail {privateString id;
}
ProductDetail
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = 2184784038009791692L;privateString productId;privateProductDetailDTO productDetail;
}
ProductDTO
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProductDetailDTO {privateString detailId;
}
ProductDetailDTO
定义映射器:
@Mapper(componentModel = "spring")
public interface Demo6Assembler {
ProductDTO toDTO(Product product);
@Mapping(target = "detailId", source = "id")
ProductDetailDTO toDetailDTO(ProductDetail detail);
}
生成代码:
@Componentpublic class Demo6AssemblerImpl implementsDemo6Assembler {
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );
productDTO.setProductDetail( toDetailDTO( product.getProductDetail() ) );//查找使用存在的转换方法
returnproductDTO;
}
@OverridepublicProductDetailDTO toDetailDTO(ProductDetail detail) {if ( detail == null) {return null;
}
ProductDetailDTO productDetailDTO= newProductDetailDTO();
productDetailDTO.setDetailId( detail.getId() );returnproductDetailDTO;
}
}
(4)多层bean之间的转换
@Mapping注解支持跨层级的属性转换,属性可以在不同层级之间切换。
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProduct {privateString productId;privateProductDetail productDetail;
}
Product
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProductDetail {privateString id;
}
ProductDetail
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = 2184784038009791692L;privateString productId;privateProductDetailDTO productDetail;
}
ProductDTO
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProductDetailDTO {privateString productId;privateString detailId;
}
ProductDetailDTO
定义映射器:
@Mapper(componentModel = "spring")public interfaceDemo7Assembler {
@Mapping(target= "productDetail.detailId", source = "productDetail.id") //声明productDetail下的属性转换规则
@Mapping(target = "productDetail.productId", source = "productId") //跨层级的属性转换,把product层级的productId放到productDetail层级
ProductDTO toDTO(Product product);
}
生成代码:
@Componentpublic classDemo7AssemblerImpl implements Demo7Assembler {
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();if ( product.getProductDetail() != null) {if ( productDTO.getProductDetail() == null) {
productDTO.setProductDetail(newProductDetailDTO() );
}
productDetailToProductDetailDTO( product.getProductDetail(), productDTO.getProductDetail() );
}if ( productDTO.getProductDetail() == null) {
productDTO.setProductDetail(newProductDetailDTO() );
}
productToProductDetailDTO( product, productDTO.getProductDetail() );
productDTO.setProductId( product.getProductId() );returnproductDTO;
}//detail的转换方法
protected voidproductDetailToProductDetailDTO(ProductDetail productDetail, ProductDetailDTO mappingTarget) {if ( productDetail == null) {return;
}
mappingTarget.setDetailId( productDetail.getId() );
}//product转成detail(更新处理)
protected voidproductToProductDetailDTO(Product product, ProductDetailDTO mappingTarget) {if ( product == null) {return;
}
mappingTarget.setProductId( product.getProductId() );
}
}
Demo7AssemblerImpl
3.3.4 自定义转换器
MapStruct支持自定义转换器,实现类型之间的转换自定义的规则。
一个自定义映射器可以定义多个映射方法,匹配时,是以方法的入参和出参进行匹配的。如果绑定的映射中,存在多个相同的入参和出参方法,将会报错。
如果多个入参或者出参方法存在继承关系,将会匹配最具体的那一个方法。
@Data
@AllArgsConstructor
@NoArgsConstructorpublic classProduct {privateString productId;private Listimages;
}
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class ProductDTO implementsSerializable {private static final long serialVersionUID = 2184784038009791692L;privateString productId;privateString images;
}
bean
定义映射器:
@Componentpublic classImageFormater {public String format(Listimages) {return String.join(",", images);
}
}
绑定转换器:
@Mapper(componentModel = "spring", uses = ImageFormater.class)public interfaceDemo8Assembler {
ProductDTO toDTO(Product product);
}
映射器实现:
@Componentpublic class Demo8AssemblerImpl implementsDemo8Assembler {
@AutowiredprivateImageFormater imageFormater;
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );//调用自定义的映射器进行映射,把list转成string
productDTO.setImages( imageFormater.format( product.getImages() ) );returnproductDTO;
}
}
3.3.5 使用限定符限定使用转换方法
自定义转换器时,存在多个相同入参和出参的方法,MapStruct无法匹配使用哪个映射方法。这时可以使用限定符绑定每个属性转换时使用的转换方法。
(1)限定符使用自定义注解实现。
声明限定符:
importorg.mapstruct.Qualifier;//映射器上的限定符
@Qualifier //标记为限定符
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)public @interfaceFormators {
}//映射方法上的限定符
@Qualifier //标记为限定符
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)public @interfaceFormatImages {
}//映射方法上的限定符
@Qualifier //标记为限定符
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)public @interfaceFormatDetails {
}
绑定限定符到映射器的方法上面:
@Component
@Formators//绑定限定符
public classCusFormater {
@FormatImages//绑定限定符
public String formatImages(Listimages) {return String.join(",", images);
}
@FormatDetails//绑定限定符
public String formatDetails(Listimages) {return String.join(",", images);
}
}
映射时,绑定限定符,定位映射方法:
@Mapper(componentModel = "spring", uses = CusFormater.class)public interfaceDemo9Assembler {
@Mapping(target= "images", qualifiedBy = FormatImages.class) //转换指定限定符,定位具体的映射方法
@Mapping(target = "details", qualifiedBy = FormatDetails.class)//转换指定限定符,定位具体的映射方法
ProductDTO toDTO(Product product);
}
生成代码:
@Componentpublic class Demo9AssemblerImpl implementsDemo9Assembler {
@AutowiredprivateCusFormater cusFormater;
@OverridepublicProductDTO toDTO(Product product) {if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );
productDTO.setImages( cusFormater.formatImages( product.getImages() ) );//定位方法
productDTO.setDetails( cusFormater.formatDetails( product.getDetails() ) );returnproductDTO;
}
}
(2)基于named注解实现(推荐)
除了使用自定义注解的方法,还可以使用@Named注解实现限定符的绑定。
@Component
@Named("CusFormater")public classCusFormater {//绑定限定符
@Named("formatImages")public String formatImages(Listimages) {return String.join(",", images);
}//绑定限定符
@Named("formatDetails")public String formatDetails(Listimages) {return String.join(",", images);
}
}
使用时绑定:
@Mapper(componentModel = "spring", uses = CusFormater.class)public interfaceDemo10Assembler {
@Mapping(target= "images", qualifiedByName = "formatImages") //转换指定限定符,定位具体的映射方法
@Mapping(target = "details", qualifiedByName = "formatDetails")//转换指定限定符,定位具体的映射方法
ProductDTO toDTO(Product product);
}
3.4 Map的映射
可以使用@MapMapping实现对key和value的分别映射:
@Mapper(componentModel = "spring")
public interfaceDemo11Assembler {
@MapMapping(valueDateFormat = "yyyy-MM-dd HH:mm:ss")
Map toDTO(Mapmap);
}
3.5 枚举值之间的转换
MapStruct可以在多个枚举值之间转换,使用@ValueMapping注解。
public enumE1 {
E1_1,
E1_2,
E1_3,
;
}
public enumE2 {
E2_1,
E2_2,
E2_3,
;
}
@Mapper(componentModel = "spring")
public interfaceDemo11Assembler {
@ValueMapping(target = "E1_1", source = "E2_1")
@ValueMapping(target = "E1_2", source = "E2_2")
@ValueMapping(target = MappingConstants.NULL, source = "E2_3") //转换成null
E1 toDTO(E2 e2);
}
生成代码:
@Component
public class Demo11AssemblerImpl implementsDemo11Assembler {
@Override
publicE1 toDTO(E2 e2) {
if ( e2 == null) {
return null;
}
E1 e1;
switch( e2 ) {
case E2_1: e1 =E1.E1_1;
break;
case E2_2: e1 =E1.E1_2;
break;
case E2_3: e1 = null;
break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " +e2 );
}
returne1;
}
}
3.6 定制Bean生成
使用MapStruct可以使用对象工厂来创建bean,同时也可以更新bean。
定义对象工厂:
public classDTOFactory {
publicProductDTO createDTO() {
ProductDTO productDTO = newProductDTO();
productDTO.setStock(0);
returnproductDTO;
}
}
使用对象工厂:
@Mapper(componentModel = "spring", uses = DTOFactory.class) //指定使用的对象工厂
public interfaceDemo13Assembler {
ProductDTO toDTO(Product product);
}
生成代码:
@Component
public class Demo13AssemblerImpl implementsDemo13Assembler {
@Autowired
privateDTOFactory dTOFactory;
@Override
publicProductDTO toDTO(Product product) {
if ( product == null) {
return null;
}
ProductDTO productDTO =dTOFactory.createDTO(); //使用对象工厂创建对象
productDTO.setProductId( product.getProductId() );
returnproductDTO;
}
}
3.7 缺省值和常量
MapStruct允许设置缺省值和常量,同时缺省值允许使用表达式。
注意:使用缺省值,源字段必须存在,否则缺省值不生效,否则应该使用常量。
@Mapper(componentModel = "spring", imports = UUID.class)
public interface Demo15Assembler {
@Mapping(target = "productId", source = "productId", defaultValue = "0") //当product的productId为null,设置为0
@Mapping(target = "random", source = "random", defaultExpression = "java(UUID.randomUUID().toString())") //缺省设置随机数
@Mapping(target = "stock", constant = "0") //固定设置为0
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd", constant = "2020-05-30") //固定格式化设置为2020-05-30,
ProductDTO toDTO(Product product);
}
@Component
public class Demo15AssemblerImpl implements Demo15Assembler {
@Override
public ProductDTO toDTO(Product product) {
if ( product == null ) {
return null;
}
ProductDTO productDTO = new ProductDTO();
if ( product.getProductId() != null ) {
productDTO.setRandom( product.getProductId() );
}
else {
productDTO.setRandom( UUID.randomUUID().toString() );
}
if ( product.getProductId() != null ) {
productDTO.setProductId( product.getProductId() );
}
else {
productDTO.setProductId( "0" );
}
productDTO.setStock( 0 );
try {
productDTO.setCreateTime( new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2020-05-30" ) );
}
catch ( ParseException e ) {
throw new RuntimeException( e );
}
return productDTO;
}
}
3.8 存在继承关系的结果处理
当返回的结果类型存在继承关系时,可以使用 @BeanMapping注解指定真实返回的结果类型。
@Mapper(componentModel = "spring")
public interfaceDemo17Assembler {
@BeanMapping(resultType = DogDTO.class) //指定返回的结果类型
Animal toDTO(Dog dog);
}
@Component
public class Demo17AssemblerImpl implementsDemo17Assembler {
@Override
publicAnimal toDTO(Dog dog) {
if ( dog == null) {
return null;
}
DogDTO animal = newDogDTO();
animal.setId( dog.getId() );
returnanimal;
}
}
3.9 映射关系继承
MapStruct允许对映射关系进行继承,使用@InheritConfiguration标记当前方法继承其他映射方法的映射关系。会自动查找相同类型映射源、映射目标的方法进行继承,如果存在多个相同类型的方法,则需要手工指定。
@Mapper(componentModel = "spring")
public interface Demo18Assembler {
@Mapping(target = "productId", source = "id")
@Mapping(target = "detail", source = "detail1")
ProductDTO toDTO(Product product);
@Mapping(target = "productId", source = "id2")
@Mapping(target = "detail", source = "detail2")
ProductDTO toDTO2(Product product);
@InheritConfiguration(name = "toDTO") //对toDTO的映射关系进行继承
@Mapping(target = "detail", source = "detail2") //对继承的关系进行重写
void update(@MappingTarget ProductDTO productDTO, Product product);
}
除了正向继承规则外,还可以进行规则逆向继承,从被继承方法的目标对象映射到源对象。
@Mapper(componentModel = "spring")
public interface Demo18Assembler {
@Mapping(target = "productId", source = "id")
@Mapping(target = "detail", source = "detail1")
ProductDTO toDTO(Product product);
@Mapping(target = "productId", source = "id2")
@Mapping(target = "detail", source = "detail2")
ProductDTO toDTO2(Product product);
@InheritInverseConfiguration(name = "toDTO") //对toDTO的映射关系进行逆继承
@Mapping(target = "detail2", source = "detail") //对逆向继承的关系进行重写
Product toEntity(ProductDTO dto);
}
3.10 复杂映射的实现
有时候我们除了普通映射外,还需要进行一些复杂的映射,如把多个字段计算映射成一个字段,或者借用一些工具进行映射的计算等。MapStruct提供了集中方式实现。
3.10.1 使用java表达式进行映射
对于复杂的映射,允许使用java表达式实现字段的映射。
注意要导入使用到的类。
@Mapper(componentModel = "spring", imports = DecimalUtils.class) //导入java表达式使用的类
public interface Demo16Assembler {
@Mapping(target = "price", expression = "java(product.getPrice1() + product.getPrice2())") //直接相加
@Mapping(target = "price2", expression = "java(DecimalUtils.add(product.getPrice1(), product.getPrice2()))") //使用工具类处理
ProductDTO toDTO(Product product);
}
生成的映射代码:
@Component
public class Demo16AssemblerImpl implements Demo16Assembler {
@Override
public ProductDTO toDTO(Product product) {
if ( product == null ) {
return null;
}
ProductDTO productDTO = new ProductDTO();
productDTO.setProductId( product.getProductId() );
productDTO.setPrice( product.getPrice1() + product.getPrice2() );
productDTO.setPrice2( DecimalUtils.add(product.getPrice1(), product.getPrice2()) );
return productDTO;
}
}
3.10.2 使用装饰器进行映射
MapStruct允许使用装饰器进行一些复杂映射,同时可以支持和Spring结合。
定义一个映射器,同时声明绑定装饰器:
@Mapper(componentModel = "spring")
@DecoratedWith(Demo18AssemblerDecorator.class) //声明绑定装饰器
public interfaceDemo18Assembler {
ProductDTO toDTO(Product product);
}
定义装饰器:
public abstract class Demo18AssemblerDecorator implementsDemo18Assembler {
@Autowired
@Qualifier("delegate") //注入mapStruct生成的转换器,原始的转换器注入spring时,会使用delegate装饰符
privateDemo18Assembler assembler;//可以获取spring的bean进行操作
@AutowiredprivateStringUtils stringUtils;
@OverridepublicProductDTO toDTO(Product product) {//调用MapStruct进行转换
ProductDTO productDTO =assembler.toDTO(product);//自定义操作
stringUtils.join(product.getName(), "-", product.getTitle());returnproductDTO;
}
}
生成装饰器代码:
@Component
@Primary//Primary修饰,方便使用时直接使用autowired注入
public class Demo18AssemblerImpl extends Demo18AssemblerDecorator implementsDemo18Assembler {
}
3.10.3 使用前后置处理实现复杂映射
使用@BeforeMapping和@AfterMapping注解可以指定映射过程的的回调方法,进行一些前置或者后置的操作。
前置回调方法的执行时机是在映射方法开始时,后置方法是在映射完成return之前。
回调方法可以直接定义在映射器内:
@Mapper(componentModel = "spring")public interfaceDemo19Assembler {
ProductDTO toDTO(Product product);
@BeforeMapping//前置执行
defaultProductDTO toDTOBefore(Product product) {
ProductDTO productDTO= newProductDTO();
productDTO.setSales(9999);returnproductDTO;
}
@AfterMapping//后置执行
default voidtoDTOAfter(Product product, @MappingTarget ProductDTO productDTO) {
productDTO.setViewName(product.getName()+ "-" +product.getTitle());
}
}
生成的实现代码如下:
@Componentpublic class Demo19AssemblerImpl implementsDemo19Assembler {
@OverridepublicProductDTO toDTO(Product product) {
ProductDTO target= toDTOBefore( product ); //前置
if ( target != null) {returntarget;
}if ( product == null) {return null;
}
ProductDTO productDTO= newProductDTO();
productDTO.setProductId( product.getProductId() );
toDTOAfter( product, productDTO );//后置
returnproductDTO;
}
}
回调方法与映射的方法的匹配规则:
(1)映射方法和回调方法没有强绑定的关系,是依靠参数类型来匹配映射方法与回调方法的。映射方法的所有入参和出参类型,能覆盖回调方法的入参,就会调用对应的回调方法,当要注意,如果回调方法的入参是映射方法的出参类型,回调方法中需要用@MappingTarget 指定,否则不会调用。
(2)回调方法是void或者返回映射方法的出参类型才能匹配,但要注意,如果返回的是映射方法的出参类型,如果执行时返回不为null,则映射方法直接返回回调方法执行结果,不会往后执行。