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

MapStruct映射器中ObjectFactory方法未识别问题排查

MapStruct抽象类映射报错:无法识别@ObjectFactory工厂方法

背景代码

单表继承的抽象实体类Hierarchy

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorOptions(insert = false)
public abstract class Hierarchy implements Serializable {

    private HierarchyId id;

    protected Hierarchy() {
    }

    protected Hierarchy(final HierarchyId id) {
        this.id = id;
    }

    @EmbeddedId
    public HierarchyId getId() {
        return id;
    }

    public void setId(final HierarchyId id) {
        this.id = id;
    }

    @Embeddable
    public static class HierarchyId implements Serializable {

        private User parent;
        private User child;
        private String type;

        public HierarchyId() {
        }

        public HierarchyId(final User parent, final User child, final String type) {
            this.parent = parent;
            this.child = child;
            this.type = type;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        public User getParent() {
            return parent;
        }

        public void setParent(final User parent) {
            this.parent = parent;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        public User getChild() {
            return child;
        }

        public void setChild(final User child) {
            this.child = child;
        }

        @Column(name = "type", nullable = false, length = 1)
        public String getType() {
            return type;
        }

        public void setType(final String type) {
            this.type = type;
        }
    }
}

自定义的MapStruct映射器HierarchyMapper

@Configuration
@Mapper
public abstract class HierarchyMapper {

    @Autowired
    private UserService userService;

    @Mapping(target = "id.parent.id", ignore = true)
    @Mapping(target = "id.child.id", ignore = true)
    @Mapping(source = "type", target = "id.type")
    @BeanMapping(qualifiedByName = "afterHierarchyMapping")
    public abstract Hierarchy mapHierarchy(HierarchyDto hierarchy);

    @ObjectFactory
    public Hierarchy createHierarchy(HierarchyDto hierarchy) {
        if (SUBTYPE_A.equals(hierarchy.getType())) {
            return new SubtypeA();
        } else if (SUBTYPE_B.equals(hierarchy.getType())) {
            return new SubtypeB();
        }
        return null;
    }

    @AfterMapping
    @Named("afterHierarchyMapping")
    void afterHierarchyMapping(@MappingTarget Hierarchy hierarchy, HierarchyDto hierarchyDto) {
        hierarchy.getId()
                  .setParent(userService.getUserById(hierarchyDto.getIdParent()));
        hierarchy.getId()
                  .setChild(userService.getUserById(hierarchyDto.getIdChild()));
    }
}

报错信息

java: The return type Hierarchy is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.

移除@BeanMapping注解后,会触发User抽象类的相同报错。


问题原因与修复方案

1. 核心原因:映射器未配置Spring组件模型

你的@Mapper注解缺少componentModel = "spring"配置,MapStruct默认采用default组件模型,不会将映射器作为Spring Bean处理,导致@ObjectFactory方法无法被正确关联和调用。

2. 修复步骤

步骤1:修正映射器的@Mapper配置

添加componentModel = "spring",确保MapStruct生成Spring兼容的实现类,同时识别@Autowired和@ObjectFactory:

@Configuration
@Mapper(componentModel = "spring") // 新增Spring组件模型配置
public abstract class HierarchyMapper {

    @Autowired
    private UserService userService;

    // 调整映射规则:完全忽略parent和child的自动映射,避免MapStruct尝试实例化抽象User类
    @Mapping(target = "id.parent", ignore = true)
    @Mapping(target = "id.child", ignore = true)
    @Mapping(source = "type", target = "id.type")
    @BeanMapping(qualifiedByName = "afterHierarchyMapping")
    public abstract Hierarchy mapHierarchy(HierarchyDto hierarchy);

    @ObjectFactory
    public Hierarchy createHierarchy(HierarchyDto hierarchy) {
        if (SUBTYPE_A.equals(hierarchy.getType())) {
            return new SubtypeA();
        } else if (SUBTYPE_B.equals(hierarchy.getType())) {
            return new SubtypeB();
        }
        // 建议抛出异常而非返回null,避免后续空指针
        throw new IllegalArgumentException("不支持的层级类型: " + hierarchy.getType());
    }

    @AfterMapping
    @Named("afterHierarchyMapping")
    void afterHierarchyMapping(@MappingTarget Hierarchy hierarchy, HierarchyDto hierarchyDto) {
        hierarchy.getId().setParent(userService.getUserById(hierarchyDto.getIdParent()));
        hierarchy.getId().setChild(userService.getUserById(hierarchyDto.getIdChild()));
    }
}

步骤2:确保子类提供无参构造器

SubtypeA和SubtypeB必须包含无参构造器(可以是protected),因为MapStruct创建实例后会通过setter填充属性:

@Entity
@DiscriminatorValue("A")
public class SubtypeA extends Hierarchy {
    protected SubtypeA() {
        super();
    }
}

步骤3:彻底忽略User字段的自动映射

之前只忽略了id.parent.id和id.child.id,但MapStruct仍会尝试映射User对象本身。改为忽略id.parent和id.child,完全交由@AfterMapping通过userService获取实例,避免触发User抽象类的实例化报错。

3. 验证方法

执行项目清理编译(如mvn clean compile),查看MapStruct生成的HierarchyMapperImpl类,确认:

  • 先调用createHierarchy方法获取具体子类实例
  • 执行完基础映射后调用afterHierarchyMapping方法

内容的提问来源于stack exchange,提问作者gb2

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 15:55:08