zoukankan      html  css  js  c++  java
  • 简化mapstruct代码: mapstruct-spring-plus

    mapstruct

    MapStruct 是一个属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。MapStruct官网地址: http://mapstruct.org/
    MapStruct 使用APT生成映射代码,其在效率上比使用反射做映射的框架要快很多。

    mapstruct spring

    MapStruct 结合spring使用,设定componentModel = "spring"即可,如下Mapper接口:

    @Mapper(componentModel = "spring")
    public interface CarDtoMapper{
        Car dtoToEntity(CarDto dto);
    }
    

    生成的映射代码如下,发现实现类上添加了@Component注解

    
    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2021-04-26T11:02:50+0800",
        comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.8.jar, environment: Java 1.8.0_211 (Oracle Corporation)"
    )
    @Component
    public class CarDtoMapperImpl implements CarDtoMapper {
    
        @Override
        public Car dtoToEntity(CarDto dto) {
            if ( dto == null ) {
                return null;
            }
    
            Car car = new Car();
    
            car.setName( dto.getName() );
    
            return car;
        }
    }
    
    

    mapstruct spring 使用的缺点

    mapstruct结合spring,在使用方式上主要是需要编写接口文件和定义函数所带来编码工作量:

    1. 需要创建mapper接口文件,这个是mapstruct框架的必须要经历的过程,代码量增加
    2. Dto和Entity之间互相转换,需要在接口中添加一个方法,并且添加上InheritInverseConfiguration注解,如下
    @InheritInverseConfiguration(name = "dtoToEntity")    
    CarDto entityToDto(Car dto);
    
    1. service 中依赖多个mapper转换,增加构造函数注入个数
    2. 覆盖已有对象,需要添加如下map方法,如下
    Car dtoMapToEntity(CarDto dto, @MappingTarget Car car)
    

    反向映射,同样需要添加如下方法

    CarDto entityMapToDto(Car dto, @MappingTarget CarDto car);
    

    理想的映射工具

    对于对象映射,有一种理想的使用方式,伪代码如下

    Car car = mapper.map(dto, Car.class);
    // or
    Car car = new Car();
    mapper.map(dto, car);
    
    //  反向映射
    CarDto dto = mapper.map(entity, CarDto.class);
    // or
    CarDto dto = new CarDto();
    mapper.map(entity, dto);
    

    只使用mapper对象,就可以解决任何对象之间的映射。

    mapstruct 官方解决方案: mapstruct-spring-extensions

    官方地址如下: https://github.com/mapstruct/mapstruct-spring-extensions
    其思路是使用spring 的 Converter接口,官方用法如下

    
    
    @Mapper(config = MapperSpringConfig.class)
    public interface CarMapper extends Converter<Car, CarDto> {
        @Mapping(target = "seats", source = "seatConfiguration")
        CarDto convert(Car car);
    }
    
      @ComponentScan("org.mapstruct.extensions.spring")
      @Component
      static class AdditionalBeanConfiguration {
        @Bean
        ConfigurableConversionService getConversionService() {
          return new DefaultConversionService();
        }
      }
    
      @BeforeEach
      void addMappersToConversionService() {
        conversionService.addConverter(carMapper);
        conversionService.addConverter(seatConfigurationMapper);
        conversionService.addConverter(wheelMapper);
        conversionService.addConverter(wheelsMapper);
        conversionService.addConverter(wheelsDtoListMapper);
      }
    
      @Test
      void shouldKnowAllMappers() {
        then(conversionService.canConvert(Car.class, CarDto.class)).isTrue();
        then(conversionService.canConvert(SeatConfiguration.class, SeatConfigurationDto.class)).isTrue();
        then(conversionService.canConvert(Wheel.class, WheelDto.class)).isTrue();
        then(conversionService.canConvert(Wheels.class, List.class)).isTrue();
        then(conversionService.canConvert(List.class, Wheels.class)).isTrue();
      }
    
      @Test
      void shouldMapAllAttributes() {
        // Given
        final Car car = new Car();
        car.setMake(TEST_MAKE);
        car.setType(TEST_CAR_TYPE);
        final SeatConfiguration seatConfiguration = new SeatConfiguration();
        seatConfiguration.setSeatMaterial(TEST_SEAT_MATERIAL);
        seatConfiguration.setNumberOfSeats(TEST_NUMBER_OF_SEATS);
        car.setSeatConfiguration(seatConfiguration);
        final Wheels wheels = new Wheels();
        final ArrayList<Wheel> wheelsList = new ArrayList<>();
        final Wheel wheel = new Wheel();
        wheel.setDiameter(TEST_DIAMETER);
        wheel.setPosition(TEST_WHEEL_POSITION);
        wheelsList.add(wheel);
        wheels.setWheelsList(wheelsList);
        car.setWheels(wheels);
    
        // When
        final CarDto mappedCar = conversionService.convert(car, CarDto.class);
    }
    

    使用 mapstruct-spring-extensions,使用 ConfigurableConversionService, 虽然解决了使用同一个对象映射,但是代码量没有解决,同时,没有提供覆盖已有对象的使用方式

    推荐 mapstruct-spring-plus

    地址: https://github.com/ZhaoRd/mapstruct-spring-plus
    这个项目参考了mapstruct-spring-extensions项目,同时使用APT技术,动态生成Mapper接口,解决编写接口的问题,提供IObejctMapper接口,提供所有的map方法。

    maven引入

    <properties>
        <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
        <io.github.zhaord.version>1.0.1.RELEASE</io.github.zhaord.version>
    </properties>
    ...
    <dependencies>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.zhaord</groupId>
            <artifactId>mapstruct-spring-plus-boot-starter</artifactId>
            <version>${io.github.zhaord.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>
                        <path>
                            <groupId>io.github.zhaord</groupId>
                            <artifactId>mapstruct-spring-plus-processor</artifactId>
                            <version>${io.github.zhaord.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    gradle 引入

    dependencies {
        ...
        compile 'org.mapstruct:mapstruct:1.4.2.Final'
        compile 'io.github.zhaord:mapstruct-spring-plus-boot-starter:1.0.1.RELEASE'
    
        annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
        testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code
    
        annotationProcessor 'io.github.zhaord:mapstruct-spring-plus-processor:1.0.1.RELEASE'
        testAnnotationProcessor 'io.github.zhaord:mapstruct-spring-plus-processor:1.0.1.RELEASE' // if you are using mapstruct in test code
        ...
    }
    

    使用案例

    使用代码如下

    
    public enum CarType {
        SPORTS, OTHER
    }
    
    @Data
    public class Car {
        private String make;
        private CarType type;
    }
    
    
    @Data
    @AutoMap(targetType = Car.class)
    public class CarDto {
        private String make;
    
        private String type;
    
    }
    
    
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(
            classes = {AutoMapTests.AutoMapTestConfiguration.class})
    public class AutoMapTests {
    
        @Autowired
        private IObjectMapper mapper;
    
        @Test
        public void testDtoToEntity() {
    
            var dto = new CarDto();
            dto.setMake("M1");
            dto.setType("OTHER");
    
            Car entity = mapper.map(dto, Car.class);
    
            assertThat(entity).isNotNull();
            assertThat(entity.getMake()).isEqualTo("M1");
            assertThat(entity.getCarType()).isEqualTo("OTHER");
    
        }
    
    
        @ComponentScan("io.github.zhaord.mapstruct.plus")
        @Configuration
        @Component
        static class AutoMapTestConfiguration {
    
    
        }
    
    
    }
    

    AutoMap 生成的接口代码

    
    @Mapper(
        config = AutoMapSpringConfig.class,
        uses = {}
    )
    public interface CarDtoToCarMapper extends BaseAutoMapper<CarDto, Car> {
      @Override
      @Mapping(
          ignore = false
      )
      Car map(final CarDto source);
    
      @Override
      @Mapping(
          ignore = false
      )
      Car mapTarget(final CarDto source, @MappingTarget final Car target);
    }
    
    

    mapstruct-spring-plus 带来的便捷

    1. 使用AutoMap注解,减少了重复代码的编写,尤其是接口文件和映射方法
    2. 依赖注入,只需要注入IObjectMapper接口即可,具体实现细节和调用方法,对客户端友好
    3. 没有丢失mapstruct的功能和效率
    4. @Mapping注解,都可以使用@AutoMapField来完成字段的映射设置,因为@AutoMapField继承自@Mapping,比如字段名称不一致、跳过映射等

    关注我的公众号,一起探索新技术

    微信公众号: abplearn QQ: 1260825783
  • 相关阅读:
    codeforces 589G G. Hiring(树状数组+二分)
    树状数组的小总结
    virtualenv
    Redis备份与恢复
    Linux 网站相关
    MySQL文章参考
    动态执行表不可访问,本会话的自动统计被禁止 。 在执行菜单里你可以禁止统计,或在v$session,v$sesstat 和 v$statname 表里获得select权限
    app已损坏,打不开。你应该将它移到废纸篓
    macos系统用virtualbox安装Linux系统无法设
    Hadoop 分布式部署HDFS-hadoop用户部署
  • 原文地址:https://www.cnblogs.com/zhaord/p/14737643.html
Copyright © 2011-2022 走看看