在日常开发中,我们会定义多种不同的Javabean,比如DTO(Data Transfer Object:数据传输对象),DO(Data Object:数据库映射对象,与数据库一一映射),VO(View Object:显示层对象,通常是 Web 向模板渲染引擎层传输的对象)等等这些对象。在这些对象与对象之间转换通常是调对象的set和get方法进行复制,这种转换通常也是很无聊的操作,如果有一个专门的工具来解决Javabean之间的转换问题,让我们从这种无聊的转换操作中解放出来。

MapStruct就是这样一个属性映射工具,用于解决上述对象之间转换问题。MapStruct官网给出的定义:MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。

1.简单使用

通常在项目中,mapStruct和lombox会同时使用,具体的maven配置如下。

  1. <properties>
  2. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  3. <maven.compiler.source>1.8</maven.compiler.source>
  4. <maven.compiler.target>1.8</maven.compiler.target>
  5. <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
  6. <org.projectlombok.version>1.18.12</org.projectlombok.version>
  7. </properties>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.mapstruct</groupId>
  11. <artifactId>mapstruct</artifactId>
  12. <version>${org.mapstruct.version}</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.projectlombok</groupId>
  16. <artifactId>lombok</artifactId>
  17. <version>${org.projectlombok.version}</version>
  18. <scope>provided</scope>
  19. </dependency>
  20. <dependency>
  21. <groupId>junit</groupId>
  22. <artifactId>junit</artifactId>
  23. <scope>test</scope>
  24. <version>4.12</version>
  25. </dependency>
  26. </dependencies>
  27. <!-- 配置lombok 和mapStruct注解处理器 -->
  28. <build>
  29. <pluginManagement>
  30. <plugins>
  31. <plugin>
  32. <groupId>org.apache.maven.plugins</groupId>
  33. <artifactId>maven-compiler-plugin</artifactId>
  34. <version>3.8.1</version>
  35. <configuration>
  36. <source>1.8</source>
  37. <target>1.8</target>
  38. <annotationProcessorPaths>
  39. <path>
  40. <groupId>org.mapstruct</groupId>
  41. <artifactId>mapstruct-processor</artifactId>
  42. <version>${org.mapstruct.version}</version>
  43. </path>
  44. <path>
  45. <groupId>org.projectlombok</groupId>
  46. <artifactId>lombok</artifactId>
  47. <version>${org.projectlombok.version}</version>
  48. </path>
  49. </annotationProcessorPaths>
  50. </configuration>
  51. </plugin>
  52. </plugins>
  53. </pluginManagement>
  54. </build>

官方maven配置:

  1. ############mapper Struct###########
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.mapstruct.examples.lombok</groupId>
  7. <artifactId>mapstruct-examples-lombok</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  12. <maven.compiler.source>1.8</maven.compiler.source>
  13. <maven.compiler.target>1.8</maven.compiler.target>
  14. <org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
  15. <org.projectlombok.version>1.18.12</org.projectlombok.version>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.mapstruct</groupId>
  20. <artifactId>mapstruct</artifactId>
  21. <version>${org.mapstruct.version}</version>
  22. </dependency>
  23. <!-- lombok dependencies should not end up on classpath -->
  24. <!-- 如果 MapStruct 生成的实现类里面,只创建了对象没有对属性进行 set 可能是版本没匹配,使用官方给的这个版本就好了 -->
  25. <dependency>
  26. <groupId>org.projectlombok</groupId>
  27. <artifactId>lombok</artifactId>
  28. <version>${org.projectlombok.version}</version>
  29. <scope>provided</scope>
  30. </dependency>
  31. <!-- IntelliJ pre 2018.1.1 requires the mapstruct processor to be present as provided dependency -->
  32. <!-- 高版本的 IDEA 就不需要添加这个依赖了 -->
  33. <!--
  34. <dependency>
  35. <groupId>org.mapstruct</groupId>
  36. <artifactId>mapstruct-processor</artifactId>
  37. <version>${org.mapstruct.version}</version>
  38. <scope>provided</scope>
  39. </dependency>
  40. -->
  41. </dependencies>
  42. <build>
  43. <pluginManagement>
  44. <plugins>
  45. <plugin>
  46. <groupId>org.apache.maven.plugins</groupId>
  47. <artifactId>maven-compiler-plugin</artifactId>
  48. <version>3.8.1</version>
  49. <configuration>
  50. <source>1.8</source>
  51. <target>1.8</target>
  52. <annotationProcessorPaths>
  53. <path>
  54. <groupId>org.mapstruct</groupId>
  55. <artifactId>mapstruct-processor</artifactId>
  56. <version>${org.mapstruct.version}</version>
  57. </path>
  58. <path>
  59. <groupId>org.projectlombok</groupId>
  60. <artifactId>lombok</artifactId>
  61. <version>${org.projectlombok.version}</version>
  62. </path>
  63. </annotationProcessorPaths>
  64. </configuration>
  65. </plugin>
  66. </plugins>
  67. </pluginManagement>
  68. </build>
  69. </project>

java代码如下:
定义Person实体

  1. @Data
  2. public class Person {
  3. private String name;
  4. private String lastName;
  5. }

定义PersonDTO

  1. @Data
  2. public class PersonDTO {
  3. private String firstName;
  4. private String lastName;
  5. }

使用MapStruct定义Person和PersonDTO之间的转换接口

  1. @Mapper
  2. public interface PersonMapper {
  3. PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
  4. @Mapping(source = "firstName",target = "name")
  5. Person personDTOToPerson(PersonDTO personDTO);
  6. }

使用上面定义的转换器,例子如下

  1. public class PersonMapperTest {
  2. @Test
  3. public void personDTOToPerson() {
  4. PersonMapper personMapper = PersonMapper.INSTANCE;
  5. PersonDTO personDTO = new PersonDTO();
  6. personDTO.setFirstName("feng");
  7. personDTO.setLastName("xiu");
  8. Person person = personMapper.personDTOToPerson(personDTO);
  9. Assert.assertEquals(person.getLastName(),personDTO.getLastName());
  10. Assert.assertEquals(person.getName(),personDTO.getFirstName());
  11. }
  12. }

从上面的例子可以看出,使用MapStruct定义一个对象转换器,分为以下几步

  1. 创建一个对象转换接口,使用@Mapper注解
  2. 定义转换方法,设置需要转换的对象作为参数,返回值是转换后的对象
  3. 使用@Mapping注解方法,设置转换对应的属性,如果属性名相同,则不需要设置。
  4. 接口中定义一个属性,使用Mappers.getMapper方获取对应的实现,方便使用。

通过上面4步,就可以定义出一个对象转换器,相比于之前来说简单很多。

2定义Mapper(Bean映射器)

上面已经看了一个简单的demo,下面我们来具体了解下,如何创建或者说定义一个对象转换器,也就是定义一个Mapper。

2.1 基本的映射

创建一个bean的转换器,只需要定义一个接口,并将需要的转换方法定义在接口中,然后使用org.mapstruct.Mapper注释对其进行注释。
比如上面的PersonMapper

  1. @Mapper
  2. public interface PersonMapper {
  3. PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
  4. @Mapping(source = "firstName",target = "name")
  5. Person personDTOToPerson(PersonDTO personDTO);
  6. }

@Mapper注解作用是:在build-time时,MapStruct会自动生成一个实现PersonMapper接口的类。
接口中定义的方法,在自动生成时,默认会将source对象(比如PersonDTO)中所有可读的属性拷贝到target(比如Person)对象中相关的属性,转换规则主要有以下俩条:

  1. 当target和source对象中属性名相同,则直接转换
  2. 当target和source对象中属性名不同,名字的映射可以通过@Mapping注解来指定。比如上面firstName映射到name属性上。

其实上面PersonMapper通过MapStruct生成的类和我们自己写一个转换类是没有什么区别,上面PersonMapper自动生成的实现类如下:

  1. public class PersonMapperImpl implements PersonMapper {
  2. public PersonMapperImpl() {
  3. }
  4. public Person personDTOToPerson(PersonDTO personDTO) {
  5. if (personDTO == null) {
  6. return null;
  7. } else {
  8. Person person = new Person();
  9. person.setName(personDTO.getFirstName());
  10. person.setLastName(personDTO.getLastName());
  11. return person;
  12. }
  13. }
  14. }

从上面可以看出,MapStruct的哲学是尽可能的生成看起来和手写的代码一样。因此,这也说明MapStruct映射对象属性使用的是getter/setter而不是反射。

正如上面例子这种显示的,在进行映射的时候,也会考虑通过@Mapping中指定的属性。如果指定的属性类型不同,MapStruct可能会通过隐式的类型转换,这个会在后面讲,或者通过调用/创建另外一个映射方法个,这个会在映射对象引用这一节说道。当一个bean的source和target属性是简单类型或者是Bean,才会创建一个新的映射方法,比如属性不能是Collection或者Map类型的属性。至于集合类型的映射将在后面讲。

MapStruct映射target和source的所有公共属性。这包括在父类型上声明的属性。

2.2 在Mapper中自定义转换属性方法

当俩种类型的映射不能通过MapStruct自动生成,我们需要自定义一些方法。自定义方法的方式主要有以下俩种。

如果其他Mapper中已经有此方法,可以在@Mapper(uses=XXXMapper.class)来调用自定义的方法,这样可以方法重用。这个后面会说。

java8或者更新的版本,可以直接在Mapper接口中添加default方法。当参数和返回值类型匹配,则生成的代码会自动调用这个方法。

例子如下

  1. @Mapper
  2. public interface CarMapper {
  3. @Mapping(...)
  4. ...
  5. CarDto carToCarDto(Car car);
  6. default PersonDto personToPersonDto(Person person) {
  7. //hand-written mapping logic
  8. }
  9. }

在MapStruct自动生成代码,需要将Person转换成PersonDTO对象时,就会直接调用default方法。
也可以使用抽象类来定义,比如上面的例子使用抽象类定义如下

  1. @Mapper
  2. public abstract class CarMapper {
  3. @Mapping(...)
  4. ...
  5. public abstract CarDto carToCarDto(Car car);
  6. public PersonDto personToPersonDto(Person person) {
  7. //hand-written mapping logic
  8. }
  9. }

2.3 多个source参数的映射方法

MapStruct也支持带有多个source参数的映射方法。这个在将多个bean合并成一个bean的时候非常有用。

例子如下:

  1. @Mapper
  2. public interface AddressMapper {
  3. @Mapping(source = "person.description", target = "description")
  4. @Mapping(source = "address.houseNo", target = "houseNumber")
  5. DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
  6. }

上面显示的就是将俩个source参数映射成一个target对象。和单个参数一样,属性映射也是通过名称。

如果多个source参数中的属性具有相同的名称,必须通过@Mapping指定哪个source里面的属性映射到target属性中。如果存在多个相同的属性,并且没有指定,则会报错。

MapStruct也支持直接引用一个source参数映射到target对象中。例子如下

  1. @Mapper
  2. public interface AddressMapper {
  3. @Mapping(source = "person.description", target = "description")
  4. @Mapping(source = "hn", target = "houseNumber")
  5. DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
  6. }

上面的例子将hn直接映射到target的houseNumber属性上。

2.4处理内嵌bean属性映射

例子如下:

  1. @Mapper
  2. public interface CustomerMapper {
  3. @Mapping( target = "name", source = "record.name" )
  4. @Mapping( target = ".", source = "record" )
  5. @Mapping( target = ".", source = "account" )
  6. Customer customerDtoToCustomer(CustomerDto customerDto);
  7. }
  1. 如果只是某一个内嵌属性的映射,可以类似@Mapping( target = “name”, source = “record.name” )这样写
  2. 如果是映射多个内嵌属性到target上,可以用.代替,表示把对应属性bean匹配的内嵌属性映射到target上

    2.5 更新Bean实例

    有时我们并不一定创建一个新的Bean,可能需要更新某一个实例。这种类型的映射我们可以通过在参数上增加一个@MappingTarget注解。例子如下:

    1. @Mapper
    2. public interface CarMapper {
    3. void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
    4. }

    这个例子会把CarDto中的属性值更新的Car对象实例上。上面的例子我们也可以将void改成Car类型返回值。

对于Collection或者Map类型,默认会将集合中所有的值清空,然后使用相关source集合中的值来填充,即CollectionMappingStrategy.ACCESSOR_ONLY策略。另外也提供了CollectionMappingStrategy.ADDER_PREFERRED 或者 CollectionMappingStrategy.TARGET_IMMUTABLE。这些策略可以在@Mapper(collectionMappingStrategy=CollectionMappingStrategy.TARGET_IMMUTABLE)来指定。

2.6 集合映射

基本的定义方式和普通的bean没什么区别,简单例子如下

  1. @Mapper
  2. public interface CarMapper {
  3. Set<String> integerSetToStringSet(Set<Integer> integers);
  4. List<CarDto> carsToCarDtos(List<Car> cars);
  5. CarDto carToCarDto(Car car);
  6. }

对应的生成方法如下

  1. //GENERATED CODE
  2. @Override
  3. public Set<String> integerSetToStringSet(Set<Integer> integers) {
  4. if ( integers == null ) {
  5. return null;
  6. }
  7. Set<String> set = new HashSet<String>();
  8. for ( Integer integer : integers ) {
  9. set.add( String.valueOf( integer ) );
  10. }
  11. return set;
  12. }
  13. @Override
  14. public List<CarDto> carsToCarDtos(List<Car> cars) {
  15. if ( cars == null ) {
  16. return null;
  17. }
  18. List<CarDto> list = new ArrayList<CarDto>();
  19. for ( Car car : cars ) {
  20. list.add( carToCarDto( car ) );
  21. }
  22. return list;
  23. }

对于Map的映射,还提供了@MapMapping注解,用于处理value的转换
具体的例子如下

  1. public interface SourceTargetMapper {
  2. @MapMapping(valueDateFormat = "dd.MM.yyyy")
  3. Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
  4. }

生成的代码如下

  1. //GENERATED CODE
  2. @Override
  3. public Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source) {
  4. if ( source == null ) {
  5. return null;
  6. }
  7. Map<Long, Date> map = new HashMap<Long, Date>();
  8. for ( Map.Entry<String, String> entry : source.entrySet() ) {
  9. Long key = Long.parseLong( entry.getKey() );
  10. Date value;
  11. try {
  12. value = new SimpleDateFormat( "dd.MM.yyyy" ).parse( entry.getValue() );
  13. }
  14. catch( ParseException e ) {
  15. throw new RuntimeException( e );
  16. }
  17. map.put( key, value );
  18. }
  19. return map;
  20. }

2.7 集合映射策略

通过@Mapping#collectionMappingStrategy设置集合的映射策略:CollectionMappingStrategy.ACCESSOR_ONLY:默认、CollectionMappingStrategy.SETTER_PREFERRED、CollectionMappingStrategy.ADDER_PREFERRED、CollectionMappingStrategy.TARGET_IMMUTABLE。
image.png

策略具体的意义如果没有看懂,可以参考下这篇文章MapStruct文档(五)——集合映射

2.8 枚举映射处理

2.8.1枚举映射枚举

直接上例子,方便理解

  1. @Mapper
  2. public interface OrderMapper {
  3. OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
  4. @ValueMappings({
  5. @ValueMapping(source = "EXTRA", target = "SPECIAL"),
  6. @ValueMapping(source = "STANDARD", target = "DEFAULT"),
  7. @ValueMapping(source = "NORMAL", target = "DEFAULT")
  8. })
  9. ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
  10. }

生成的代码如下

  1. // GENERATED CODE
  2. public class OrderMapperImpl implements OrderMapper {
  3. @Override
  4. public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) {
  5. if ( orderType == null ) {
  6. return null;
  7. }
  8. ExternalOrderType externalOrderType_;
  9. switch ( orderType ) {
  10. case EXTRA: externalOrderType_ = ExternalOrderType.SPECIAL;
  11. break;
  12. case STANDARD: externalOrderType_ = ExternalOrderType.DEFAULT;
  13. break;
  14. case NORMAL: externalOrderType_ = ExternalOrderType.DEFAULT;
  15. break;
  16. case RETAIL: externalOrderType_ = ExternalOrderType.RETAIL;
  17. break;
  18. case B2B: externalOrderType_ = ExternalOrderType.B2B;
  19. break;
  20. default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
  21. }
  22. return externalOrderType_;
  23. }
  24. }

默认情况下,如果存在不匹配的情形,则直接抛出异常。这种默认行为是可以被修改的,主要有以下三种策略

  1. MappingConstants.NULL : 处理null值,
  2. MappingConstants.ANY_REMAINING : 处理所有未被定义或者名字匹配不上的
  3. MappingConstants.ANY_UNMAPPED :处理任何违背匹配的情形

    2.8.2枚举与String之间的映射

    枚举到字符串的映射,不支持MappingConstants.ANY_REMAINING ```java @Mapper public interface TestMapper { @ValueMappings({
    1. @ValueMapping(source = "able_status", target = "PERFECT"),
    2. @ValueMapping(source = MappingConstants.NULL, target = "PASS"),
    3. @ValueMapping(source = "failed_status", target = MappingConstants.NULL),
    4. @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "normal"),
    }) String toEnum(DisableStatus disableStatus);

}

@Component public class TestMapperImpl implements TestMapper { @Override public String toEnum(DisableStatus disableStatus) { if ( disableStatus == null ) { return “PASS”; } String string; switch ( disableStatus ) { case able_status: string = “PERFECT”; break; case failed_status: string = null; break; default: string = “normal”; } return string; } }

  1. 字符串到枚举的映射
  2. ```java
  3. @Mapper
  4. public interface TestMapper {
  5. @ValueMappings({
  6. @ValueMapping(source = "PERFECT", target = "able_status"),
  7. @ValueMapping(source = "PASS", target = MappingConstants.NULL),
  8. @ValueMapping(source = MappingConstants.NULL, target = "failed_status"),
  9. @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "normal_status"),
  10. })
  11. DisableStatus toEnum(String disableStatus);
  12. }
  13. @Component
  14. public class TestMapperImpl implements TestMapper {
  15. @Override
  16. public DisableStatus toEnum(String disableStatus) {
  17. if ( disableStatus == null ) {
  18. return DisableStatus.failed_status;
  19. }
  20. DisableStatus disableStatus1;
  21. switch ( disableStatus ) {
  22. case "PERFECT": disableStatus1 = DisableStatus.able_status;
  23. break;
  24. case "PASS": disableStatus1 = null;
  25. break;
  26. default: disableStatus1 = DisableStatus.normal_status;
  27. }
  28. return disableStatus1;
  29. }
  30. }
  31. @Mapper
  32. public interface TestMapper {
  33. @ValueMappings({
  34. @ValueMapping(source = "PERFECT", target = "able_status"),
  35. @ValueMapping(source = "PASS", target = MappingConstants.NULL),
  36. @ValueMapping(source = MappingConstants.NULL, target = "failed_status"),
  37. @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "normal_status"),
  38. })
  39. DisableStatus toEnum(String disableStatus);
  40. }
  41. @Component
  42. public class TestMapperImpl implements TestMapper {
  43. @Override
  44. public DisableStatus toEnum(String disableStatus) {
  45. if ( disableStatus == null ) {
  46. return DisableStatus.failed_status;
  47. }
  48. DisableStatus disableStatus1;
  49. switch ( disableStatus ) {
  50. case "PERFECT": disableStatus1 = DisableStatus.able_status;
  51. break;
  52. case "PASS": disableStatus1 = null;
  53. break;
  54. case "able_status": disableStatus1 = DisableStatus.able_status;
  55. break;
  56. case "disable_status": disableStatus1 = DisableStatus.disable_status;
  57. break;
  58. case "normal_status": disableStatus1 = DisableStatus.normal_status;
  59. break;
  60. case "failed_status": disableStatus1 = DisableStatus.failed_status;
  61. break;
  62. case "ok_status": disableStatus1 = DisableStatus.ok_status;
  63. break;
  64. case "fine_status": disableStatus1 = DisableStatus.fine_status;
  65. break;
  66. default: disableStatus1 = DisableStatus.normal_status;
  67. }
  68. return disableStatus1;
  69. }
  70. }

2.8.3自定义名称转换

可以通过删除或添加源枚举字符串的前后缀来映射目标枚举对象。

  1. public enum LevelEnum {
  2. able(1, "完美"),
  3. disable(2, "合格"),
  4. normal(3, "普通"),
  5. failed(4, "不及格"),
  6. ok(5, "还行"),
  7. fine(6, "可以");
  8. private Integer code;
  9. private String desc;
  10. LevelEnum(Integer code, String desc) {
  11. this.code = code;
  12. this.desc = desc;
  13. }
  14. public Integer getCode() {
  15. return code;
  16. }
  17. public void setCode(Integer code) {
  18. this.code = code;
  19. }
  20. public String getDesc() {
  21. return desc;
  22. }
  23. public void setDesc(String desc) {
  24. this.desc = desc;
  25. }
  26. }
  27. public enum DisableStatus {
  28. able_status(1, "完美"),
  29. disable_status(2, "合格"),
  30. normal_status(3, "普通"),
  31. failed_status(4, "不及格"),
  32. ok_status(5, "还行"),
  33. fine_status(6, "可以");
  34. private Integer code;
  35. private String desc;
  36. DisableStatus(Integer code, String desc) {
  37. this.code = code;
  38. this.desc = desc;
  39. }
  40. }
  41. @Mapper
  42. public interface TestMapper {
  43. @EnumMapping(nameTransformationStrategy = "stripSuffix", configuration = "_status")
  44. LevelEnum toEnum(DisableStatus disableStatus);
  45. }
  46. @Component
  47. public class TestMapperImpl implements TestMapper {
  48. @Override
  49. public LevelEnum toEnum(DisableStatus disableStatus) {
  50. if ( disableStatus == null ) {
  51. return null;
  52. }
  53. LevelEnum levelEnum;
  54. switch ( disableStatus ) {
  55. case able_status: levelEnum = LevelEnum.able;
  56. break;
  57. case disable_status: levelEnum = LevelEnum.disable;
  58. break;
  59. case normal_status: levelEnum = LevelEnum.normal;
  60. break;
  61. case failed_status: levelEnum = LevelEnum.failed;
  62. break;
  63. case ok_status: levelEnum = LevelEnum.ok;
  64. break;
  65. case fine_status: levelEnum = LevelEnum.fine;
  66. break;
  67. default: throw new IllegalArgumentException( "Unexpected enum constant: " + disableStatus );
  68. }
  69. return levelEnum;
  70. }
  71. }

@EnumMapping#nameTransformationStrategy支持的参数有:suffix(添加源后缀)、stripSuffix(删除源后缀)、prefix(添加源前缀)、stripPrefix(删除源前缀)。

3.检索映射器

前面已经了解如何自定义对象转换器,接下来看看如何使用已经定义好的对象转换器。

3.1非依赖注入的方式

当我们不使用DI框架,Mapper实例可以通过org.mapstruct.factory.Mappers。只需要调用getMapper方法,传递接口类型的mapper就可以获得MapStruct自动生成的Mapper
像前面的例子,我们可以定义INSTANCE属性用于调用方法。例如

  1. @Mapper(componentModel = "default")
  2. public interface CarMapper {
  3. CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
  4. CarDto carToCarDto(Car car);
  5. }

使用的方式和普通的spring bean一样,

  1. @AutoWired
  2. private CarMapper mapper;

3.2注入策略

当使用DI注入策略模式时,可以选择field和constructor俩种注入方式。这个可以被@Mapper或者@MapperConfig注解来指定。
使用constructor注入的例子如下:

  1. @Mapper(componentModel = "spring", uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
  2. public interface CarMapper {
  3. CarDto carToCarDto(Car car);
  4. }

生成的映射器将注入uses属性中定义的所有类。当使用InjectionStrategy#CONSTRUCTOR,构造函数将具有适当的注解,而字段则没有。当使用InjectionStrategy#FIELD,注解字段位于field本身。目前,默认的注入策略是field注入。建议使用构造函数注入来简化测试。

3.3检索总结

检索映射器主要有以下几种,支持的值包括:

  1. default:通过Mapper#getMapper(class)来获取实例
  2. cdi:生成的映射器是一个应用程序范围的CDI bean,可以通过@Inject进行检索
  3. spring:生成的映射器是一个单例范围的spring bean,可以通过@Autowired进行检索
  4. jsr330:生成的映射器用{@code@Named}注释,可以通过@Inject检索,

这些检索策略可以通过@Mapper(componentModel=””)来指定,也可以在maven的配置参数里面指定。