在Mapstruct中如何在一个Mapper中注入另一个Mapper?
看起来你在使用MapStruct处理多参数聚合映射时,遇到了依赖Mapper找不到的问题,我来帮你捋清楚怎么解决~
问题分析
你已经通过uses = [MyMapper::class]告诉MapStruct,MyAggregateMapper需要依赖MyMapper,这一步是正确的。但你在expression里直接写myMapper.mapMyTarget(...)时,生成的代码报错找不到myMapper变量,这是因为在手写的Java表达式中,MapStruct不会自动将依赖的Mapper暴露为可用的上下文变量(或者在Kotlin+MapStruct的组合场景下,变量引用的写法需要调整)。
最优解决方案:避免手写Expression,用MapStruct原生映射规则
其实完全不需要写Java表达式,利用MapStruct的多参数映射和qualifiedByName就能优雅解决:
因为MyAggregateMapper的myMapFoo方法已经包含了mySource、myContext1、myContext2这三个MyMapper.mapMyTarget需要的参数,我们只需要给myTarget字段的映射指定要调用的MyMapper中的方法(通过qualifiedByName匹配@Named("mapMyTarget")的方法)即可,MapStruct会自动帮你传递参数。
修改后的MyAggregateMapper代码如下:
@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.ERROR, uses = [MyMapper::class] ) interface MyAggregateMapper { @Named("myAggregateMapper") @Mapping(target = "desc", source = "descParam.desc") // 直接指定使用MyMapper中名为mapMyTarget的方法,MapStruct会自动匹配所需参数 @Mapping(target = "myTarget", qualifiedByName = ["mapMyTarget"]) fun myMapFoo( mySource: MySource, myContext1: MyContext1, myContext2: MyContext2, descParam: MyAggregateDescriptionParam ): MyAggregateTarget }
这样MapStruct生成的实现类会自动调用myMapper.mapMyTarget(mySource, myContext1, myContext2)来设置myTarget字段,完全不需要手写容易出错的Java表达式。
如果你坚持要用Expression的解决方案
如果因为某些场景必须使用expression,只需要在表达式中加上this.来引用实例变量即可(因为依赖的MyMapper是注入到MyAggregateMapperImpl中的实例变量):
@Mapping(target = "myTarget", expression = "java(this.myMapper.mapMyTarget(mySource,myContext1,myContext2))")
这样生成的代码中会正确引用注入的myMapper实例,不会再出现找不到变量的错误。
为什么原来的写法会报错?
当你使用uses = [MyMapper::class]时,MapStruct会在生成的MyAggregateMapperImpl中注入MyMapper作为实例变量:
private final MyMapper myMapper; public MyAggregateMapperImpl(MyMapper myMapper) { this.myMapper = myMapper; }
但手写的Java表达式中直接写myMapper时,在生成的代码里没有明确的上下文引用(MapStruct不会自动补全this.),所以编译器会认为myMapper是局部变量而找不到它,加上this.就能明确引用实例变量了。
内容来源于stack exchange

