You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 20:54:25