我想将
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;
}
}
您可以完全跳过传递
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
已经从父映射器创建了所有必要的较小映射器...
坦率地说,我怀疑你能否通过
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;
}
}
也许看看这个。您可以使用单个注释转换两个类。