MapStruct子类映射中如何通过qualifiedByName指定调用方法
问题场景
现有三个同结构的子Mapper:CsgoDetailsApiMapper、LolDetailsApiMapper、DotaDetailsApiMapper,每个都包含两个转换方法:toApi和被@Named("toMatchApiMissing")标记的toMatchApiMissing。
以CsgoDetailsApiMapper为例:
@Mapper(uses = ApiMapper.class, builder = @Builder(disableBuilder = true)) public interface CsgoDetailsApiMapper { @Mapping(target = "title", source = "match.title") @Mapping(target = "status", source = "match.state") @Mapping(target = "teams", source = "match.teams") @Mapping(target = "games", source = "gameDetails") @Mapping(target = "id", source = "match.id") @Mapping(target = "facts", source = "matchDetails") Match toApi(CsgoMatchDetails matchDetails); @Named("toMatchApiMissing") @Mapping(target = "title", source = "match.title") @Mapping(target = "status", source = "match.state") @Mapping(target = "teams", source = "match.teams") @Mapping(target = "games", source = "gameDetails", qualifiedByName = "toGameApiMissing") @Mapping(target = "id", source = "match.id") @Mapping(target = "facts", source = "matchDetails") Match toMatchApiMissing(CsgoMatchDetails matchDetails); // 其他方法... }
同时存在一个统一的DetailsApiMapper,通过@SubclassMapping处理多态转换:
@Mapper(uses = { LolDetailsApiMapper.class, CsgoDetailsApiMapper.class, DotaDetailsApiMapper.class }) public interface DetailsApiMapper { @BeanMapping(unmappedTargetPolicy = IGNORE) @SubclassMapping(target = Match.class, source = LolMatchDetails.class) @SubclassMapping(target = Match.class, source = CsgoMatchDetails.class) @SubclassMapping(target = Match.class, source = DotaMatchDetails.class) Match toApi(MatchDetails matchDetails); @BeanMapping(unmappedTargetPolicy = IGNORE) @SubclassMapping(target = Match.class, source = LolMatchDetails.class) @SubclassMapping(target = Match.class, source = CsgoMatchDetails.class) @SubclassMapping(target = Match.class, source = DotaMatchDetails.class) Match toMatchApiMissing(MatchDetails matchDetails); }
需求是让DetailsApiMapper.toMatchApiMissing调用对应子Mapper中的toMatchApiMissing方法,而非默认的toApi方法,之前尝试在@BeanMapping中添加qualifiedByName="toMatchApiMissing"未生效。
解决方案
问题出在qualifiedByName的作用层级:当使用@SubclassMapping处理多态映射时,需要给每个@SubclassMapping单独指定限定符,而非仅在@BeanMapping上配置。
修改DetailsApiMapper的toMatchApiMissing方法如下:
@Mapper(uses = { LolDetailsApiMapper.class, CsgoDetailsApiMapper.class, DotaDetailsApiMapper.class }) public interface DetailsApiMapper { // 原toApi方法保持不变 @BeanMapping(unmappedTargetPolicy = IGNORE) @SubclassMapping(target = Match.class, source = LolMatchDetails.class, qualifiedByName = "toMatchApiMissing") @SubclassMapping(target = Match.class, source = CsgoMatchDetails.class, qualifiedByName = "toMatchApiMissing") @SubclassMapping(target = Match.class, source = DotaMatchDetails.class, qualifiedByName = "toMatchApiMissing") Match toMatchApiMissing(MatchDetails matchDetails); }
原理说明
MapStruct中,@SubclassMapping的配置优先级高于@BeanMapping的全局配置。给每个子类映射条目单独添加qualifiedByName后,MapStruct会精准匹配到对应子Mapper中被@Named("toMatchApiMissing")标记的方法,而非默认选择名称匹配的toApi方法。
同时需确保:
- 子Mapper中的
@Named注解导入的是org.mapstruct.Named - 所有子Mapper已正确声明在
DetailsApiMapper的uses属性中
内容的提问来源于stack exchange,提问作者Alex P.

