MapStruct Mapper 作为 Spring 框架转换器 - 可以惯用吗?

问题描述 投票:0回答:4

我想将

MapStruct
映射器与 Spring 的 转换模型 结合起来。所以我将每个
Mapper
接口声明为 Spring
Converter
的扩展:

@Mapper
public interface CarMapper extends Converter<Car, CarDto> {    
    @Override
    CarDto convert(Car car);    
}

然后我可以通过注入标准来使用映射器 bean

ConversionService
:

class CarWarehouse {
    @Autowired
    private ConversionService conversionService;

    ...

    public CarDto getCarInformation(Car car) {
        return conversionService.convert(car, CarDto.class);
    }
}

这工作得很好,但我想知道是否有一种方法可以避免通过

uses
属性直接将一些映射器注入到其他映射器中。我想做的是告诉映射器
use
ConversionService
来雇用另一个映射器。但是,由于
ConversionService
convert
方法与 MapStruct 的映射方法标准模式不匹配,因此代码生成插件在查找子映射时无法识别它可以使用该服务。基本上,我想做的是写

@Mapper(uses=ConversionService.class)
public interface ParentMapper extends Converter<Parent, ParentDto>

而不是

@Mapper(uses={ChildMapper1.class, ChildMapper2.class, ChildMapper3.class})
public interface ParentMapper extends Converter<Parent, ParentDto>

有没有办法实现这个目标?

编辑

既然有人问了,假设我有一个如上定义的

CarMapper
,类型为
Car
CarDto
,分别具有类型为
wheel
Wheel
的属性
WheelDto
。然后我希望能够像这样定义另一个映射器:

@Mapper
public interface WheelMapper extends Converter<Wheel, WheelDto> {    
    @Override
    WheelDto convert(Wheel wheel);    
}

现在,我必须显式添加此映射器:

@Mapper(uses = WheelMapper.class)
public interface CarMapper extends Converter<Car, CarDto>

然后将为生成的

CarMapperImpl
提供
@Autowired
类型的
WheelMapper
成员,将调用该成员来映射属性
wheel

但是,我想要的是生成的代码看起来有点像这样:

@Component
public class CarMapperImpl implements CarMapper {
    @Autowired
    private ConversionService conversionService;
    @Override
    public CarDto convert(Car car) {
        CarDto carDto = new CarDto();
        carDto.setWheel(conversionService.convert(car.getWheel(), WheelDto.class);
        return carDto;
    }
}
java spring mapstruct
4个回答
23
投票

我问这个问题已经一年多了,但现在我们在 MapStruct 项目本身中找到了答案 - MapStruct Spring Extensions 项目。

项目中提供了

CarMapper
示例 作为示例。


1
投票

您可以完全跳过传递

WheelMapper
,当您只有
CarMapper
时,生成的
CarMapperImpl
也将包含映射
Wheel
<->
WheelDto
的逻辑。无需将任何内容传递给
uses
,使您的问题过时。

carDto.setWheel( wheelToWheelDto( car.getWheel() ) );

使用类似的方法;

protected WheelDto wheelToWheelDto(Wheel wheel) {
    if ( wheel == null ) {
        return null;
    }

    WheelDto wheelDto = new WheelDto();

    wheelDto.setName( wheel.getName() );

    return wheelDto;
}

我确实尝试过通过

ConversionService
来实现
MapStruct
的智能注入,但我觉得不太可能。您需要
MapStruct
的支持才能实现这样的壮举。它甚至不考虑注射
ConversionService
。也许已经实现并使用
ConversionService
的自定义通用映射器可能会起作用,但我无法做到这一点!虽然我没有看到任何原因,因为
MapStruct
已经从父映射器创建了所有必要的较小映射器...


0
投票

坦率地说,我怀疑你能否通过

ConversionService
实现将
MapStruct
自动连接到生成的映射器中。您在问题中描述的方式(通过
uses
注释属性连接各个映射器)可能是
MapStruct
开箱即用的最佳方式。

但是,如果您绝对需要使用 ConversionService 来执行某些 DTO 的转换(例如,如果您有一些遗留转换器,您不想重构映射器),则有 解决方法

。基本上,您可以使用 
Mappers.getMapper
静态工厂的组合来获取
ConversionService
的实例和映射器接口中的
default
方法,以使用
ConversionService
实例:

@Mapper(componentModel = "spring")
public interface CarMapper extends Converter<Car, CarDto> {

    ConversionService CONVERSION_SERVICE = Mappers.getMapper(ConversionService.class);

    @Override
    default CarDto convert(Car car) {
        if (car == null) {
            return null;
        }

        CarDto carDto = new CarDto();

        carDto.setEngine(CONVERSION_SERVICE.convert(car.getEngine(), EngineDto.class));
        carDto.setWheel(CONVERSION_SERVICE.convert(car.getWheel(), WheelDto.class));

        return carDto;
    }
}

注意:如您所见,解决方法需要编写

CarMapper
代码。因此,在我看来,带有
uses
注释属性的解决方案是更干净的方法。例如,通过定义以下接口,您可以获得几乎相同的结果:

@Mapper(componentModel = "spring", 
        uses = {EngineMapper.class, WheelMapper.class}, 
        injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper extends Converter<Car, CarDto> {
    @Override
    CarDto convert(Car car);

生成的映射器:

@Component
public class CarMapperImpl implements CarMapper {

    private final EngineMapper engineMapper;
    private final WheelMapper wheelMapper;

    @Autowired
    public CarMapperImpl(EngineMapper engineMapper, WheelMapper wheelMapper) {

        this.engineMapper = engineMapper;
        this.wheelMapper = wheelMapper;
    }

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

        CarDto carDto = new CarDto();

        carDto.setEngine(engineMapper.convert(car.getEngine()));
        carDto.setWheel(wheelMapper.convert(car.getWheel()));

        return carDto;
    }
}

0
投票

也许看看这个。您可以使用单个注释转换两个类。

https://www.mapstruct.plus/en/

© www.soinside.com 2019 - 2024. All rights reserved.