MapStruct多源字段三级映射 不使用expression的实现方案
示例对象
public class Source { String fromPersonFirstName; String fromPersonLastName; String toPersonFirstName; String toPersonLastName; String message; } public class Target { Person from; Person to; String message; } public class Person { String firstName; String lastName; } @Mapper(componentModel = "spring") public interface PersonMapper { Person toPerson(String firstName, String lastName); }
问题描述
如何尽可能依托MapStruct原生能力,以简洁的方式完成Source到Target的映射,全程不使用expression表达式?
理想预期是通过类似如下配置,直接告知MapStruct完成映射逻辑:
@Mapper(componentModel = "spring", uses = PersonMapper.class) public interface ExampleMapper { @Mapping(target = "from", source = "fromPersonFirstName, fromPersonLastName") @Mapping(target = "to", source = "toPersonFirstName, toPersonLastName") Target toTarget(Source s); }
已尝试为PersonMapper及其toPerson方法添加对应注解,在toTarget方法上通过qualifiedByName指定对应的类名、方法名,但无法明确如何为该映射方法传入多个源字段。
也考虑过在本地定义携带qualifiedByName标识的toPerson方法,或者将ExampleMapper修改为抽象类后通过@Autowired注入其他映射器,但始终没有解决多源字段传入映射方法的问题。
已知expression表达式可以覆盖这类场景,但除非替代方案复杂度远高于使用表达式,否则不希望使用expression。
解决方案
以下两种均为MapStruct官方支持的原生方案,全程不需要使用expression:
- 方案一:嵌套属性直接映射(最简洁,无额外代码,兼容MapStruct 1.0+全版本)
MapStruct原生支持嵌套对象的自动实例化与属性映射,不需要额外引入PersonMapper,直接按嵌套路径配置映射规则即可。MapStruct生成代码时会自动创建Person实例,完成对应字段赋值:
@Mapper(componentModel = "spring") public interface ExampleMapper { @Mapping(target = "from.firstName", source = "fromPersonFirstName") @Mapping(target = "from.lastName", source = "fromPersonLastName") @Mapping(target = "to.firstName", source = "toPersonFirstName") @Mapping(target = "to.lastName", source = "toPersonLastName") Target toTarget(Source s); }
生成的映射代码会自动做非空判断,实例化Person对象后完成字段赋值,完全满足基础映射需求。
- 方案二:复用已有PersonMapper逻辑(适合Person映射规则复杂、需要全局统一复用的场景)
如果需要复用已定义的PersonMapper#toPerson逻辑,避免重复编写Person的映射规则,可以结合@Named与@Context注解实现,不需要硬编码表达式:
@Mapper(componentModel = "spring", uses = PersonMapper.class) public interface ExampleMapper { @Mapping(target = "from", source = "s", qualifiedByName = "buildFromPerson") @Mapping(target = "to", source = "s", qualifiedByName = "buildToPerson") Target toTarget(Source s); @Named("buildFromPerson") default Person buildFromPerson(Source s, @Context PersonMapper personMapper) { return personMapper.toPerson(s.getFromPersonFirstName(), s.getFromPersonLastName()); } @Named("buildToPerson") default Person buildToPerson(Source s, @Context PersonMapper personMapper) { return personMapper.toPerson(s.getToPersonFirstName(), s.getToPersonLastName()); } }
其中@Context标注的参数会由MapStruct自动从Spring容器/已引入的Mapper实例中注入,不需要手动传参,所有映射逻辑都是显式Java代码,没有字符串形式的硬编码方法调用,方便后续重构与调试。
注:预期的
source = "字段1, 字段2"的多字段直接传参写法,目前MapStruct不支持单源参数下的自动多参数方法匹配,上述两种方案均为官方文档明确标注的标准用法,未使用非公开能力。
内容的提问来源于stack exchange,提问作者Kalec
相关产品推荐
相关产品推荐

