MapStruct学习笔记(1):对象映射就这么简单

2022/12/21 00:08 AM

当我们定义多层架构时,我们往往会针对不同层级定义不同的实体对象。如DTO、Entity(DO)以及VO。从而不同对象之间的转换就成为了头等大事。
在充血模型中,我们一般会把转换方法放入具体对象中。

classDiagram
class Dto {
    Vo toVo()
    Entity toEntity()
}
class Vo {
    Dto toDto()
}
class Entity {
    Dto toDto()
}

然而也有部分情况下我们会专门引入转换类来负责对象之间的转换。

classDiagram
class Dto
class Vo
class Entity
class Converter {
    Vo toVo(Dto dto)
    Dto toDto(Entity entity)
    Entity toEntity(Dto dto)
}

针对每一个实体模型,我们都要单独形成Convertor类来负责对象之间的转换关系(数据映射)。所以一个好用的动态映射框架对我们提高代码效率有很大的益处。

MapStruct

根据我的Java开发经验,我非常喜欢 MapStruct ,它不仅仅可以帮我快速做好对象之间的映射关系、还可以把一些过程数据构造、格式转换等操作一并处理,让我更关注于业务代码本身。
MapStruct 是用于生成类型安全的映射Bean类的Java注释处理器。

与动态映射框架对比,MapStruct 能够带来以下优势:

  • 通过使用普通方法调用而不是反射所以执行速度更快
  • 编译时类型的安全性:只能映射限定的属性,不会超边界映射对象
  • 如果映射有问题会在在构建阶段指出明确的错误

就如我所说,这个 MapStruct 能做到的事情非常多,对我来说目前为止我只用到了它的皮毛。所以我想通过一个系列,从头开始学习整理 MapStruct 的官方文档。

安装

凡事从安装开始!

MapStruct是基于JSR 269的Java注释处理器,因此可以在命令行构建(Javac,Ant,Maven等)以及IDE内部使用。

Maven

如果你的构建工具是Maven,你需要在你的pom.xml文件使用MapStruct依赖,如下:

...
<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

Grandle

将以下内容添加到你的gradle构建文件中:

...
plugins {
    ...
    id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse
}

dependencies {
    ...
    implementation "org.mapstruct:mapstruct:${mapstructVersion}"
    annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"

    // If you are using mapstruct in test code
    testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}
...

定义 Mapper

我们来定义一个最基础的Mapper吧。

@Mapper
public ixnterface CarMapper {

    @Mapping(source = "make", target = "manufacturer")
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);

    @Mapping(source = "name", target = "fullName")
    PersonDto personToPersonDto(Person person);
}

构建后,MapStruct自动给我们生成映射的实现类

// GENERATED CODE
public class CarMapperImpl implements CarMapper {

    @Override
    public CarDto carToCarDto(Car car) {
        if ( car == null ) {
            return null;
        }

        CarDto carDto = new CarDto();

        if ( car.getFeatures() != null ) {
            carDto.setFeatures( new ArrayList<String>( car.getFeatures() ) );
        }
        carDto.setManufacturer( car.getMake() );
        carDto.setSeatCount( car.getNumberOfSeats() );
        carDto.setDriver( personToPersonDto( car.getDriver() ) );
        carDto.setPrice( String.valueOf( car.getPrice() ) );
        if ( car.getCategory() != null ) {
            carDto.setCategory( car.getCategory().toString() );
        }
        carDto.setEngine( engineToEngineDto( car.getEngine() ) );

        return carDto;
    }

    @Override
    public PersonDto personToPersonDto(Person person) {
        //...
    }

    private EngineDto engineToEngineDto(Engine engine) {
        if ( engine == null ) {
            return null;
        }

        EngineDto engineDto = new EngineDto();

        engineDto.setHorsePower(engine.getHorsePower());
        engineDto.setFuel(engine.getFuel());

        return engineDto;
    }
}

是不是很神奇?这是官方最快捷的案例。其实要弄懂MapStruct的映射规则以及编写机制,我们可以大大提高日常开发过程中的对象转换效率。让我们的代码更加简洁更关注于业务模型的设计工作当中,告别这些繁琐的转换类的编写。

今天就带大家看到这里。咱们下期继续深入学习MapStruct。