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

MapStruct能否传递源/目标外参数?复用代码遇困惑

问题描述

我想升级我的MapStruct映射器,通过特定类消除重复代码,但不知道怎么实现。我的场景里每个映射器都需要用专属类,另外也不清楚能不能使用/传递源和目标之外的其他内容。

现有示例映射器:

@Named("idsToActivity")
default List<Activity> idsToActivity(List<Long> longs) {
    return longs.stream().map(id -> {
        if (id == null) {
            return null;
        }
        Activity activity = new Activity();
        activity.setId(id);
        return activity;
    }).collect(Collectors.toList());
}

我的尝试代码:

private List<?> mapperIdToEntity(List<Long> longs){
    return longs.stream()
            .map(id -> {
                if (id == null) {
                    return null;
                }
                ClassFromMapstruct entity = new ClassFromMapstruct();
                entity.setId(id);
                return entity;
            }).collect(Collectors.toList());
}

解决方案

1. 泛型+实体工厂提取通用逻辑

把ID转实体的重复逻辑抽成通用工具方法,通过泛型和实体工厂适配不同实体类,每个映射器直接复用:

// 通用工具类
public class EntityMapperUtils {
    @FunctionalInterface
    public interface EntityFactory<T> {
        T create();
    }

    public static <T> List<T> idsToEntities(List<Long> ids, EntityFactory<T> factory) {
        if (ids == null) {
            return null;
        }
        return ids.stream()
                .map(id -> {
                    if (id == null) {
                        return null;
                    }
                    T entity = factory.create();
                    // 假设所有实体都有setId方法,若需兼容更多场景可改用BeanUtils或反射工具类
                    try {
                        Method setIdMethod = entity.getClass().getMethod("setId", Long.class);
                        setIdMethod.invoke(entity, id);
                    } catch (Exception e) {
                        throw new RuntimeException("设置实体ID失败", e);
                    }
                    return entity;
                })
                .collect(Collectors.toList());
    }
}

映射器中调用示例:

@Mapper
public interface ActivityMapper {
    @Named("idsToActivity")
    default List<Activity> idsToActivity(List<Long> longs) {
        return EntityMapperUtils.idsToEntities(longs, Activity::new);
    }
}

2. 用MapStruct的@Context传递额外参数

如果需要传递源、目标之外的内容(比如实体类类型、配置信息),可以用MapStruct的@Context注解实现:

@Mapper
public interface GenericEntityMapper {
    // 单个ID转实体
    default <T> T idToEntity(Long id, @Context Class<T> entityClass) {
        if (id == null) {
            return null;
        }
        try {
            T entity = entityClass.getDeclaredConstructor().newInstance();
            Method setIdMethod = entityClass.getMethod("setId", Long.class);
            setIdMethod.invoke(entity, id);
            return entity;
        } catch (Exception e) {
            throw new RuntimeException("创建实体失败", e);
        }
    }

    // 批量ID转实体
    default <T> List<T> idsToEntities(List<Long> ids, @Context Class<T> entityClass) {
        if (ids == null) {
            return null;
        }
        return ids.stream()
                .map(id -> idToEntity(id, entityClass))
                .collect(Collectors.toList());
    }
}

映射器中使用示例:

@Mapper(uses = GenericEntityMapper.class)
public interface ActivityMapper {
    default List<Activity> mapIdsToActivities(List<Long> ids) {
        return idsToEntities(ids, Activity.class);
    }
}

3. 自定义MapStruct转换器

如果想让MapStruct自动识别转换逻辑,可定义通用Converter:

@Converter
public class IdToEntityConverter<T> implements Converter<Long, T> {
    private final Class<T> entityClass;

    public IdToEntityConverter(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    @Override
    public T convert(Long id) {
        if (id == null) {
            return null;
        }
        try {
            T entity = entityClass.getDeclaredConstructor().newInstance();
            Method setIdMethod = entityClass.getMethod("setId", Long.class);
            setIdMethod.invoke(entity, id);
            return entity;
        } catch (Exception e) {
            throw new RuntimeException("转换失败", e);
        }
    }
}

映射器中指定使用转换器(Spring环境可通过@Bean注入转换器实例):

@Mapper(componentModel = "spring")
public interface ActivityMapper {
    ActivityMapper INSTANCE = Mappers.getMapper(ActivityMapper.class);

    @Mapping(target = "activities", source = "activityIds", converter = IdToEntityConverter.class)
    SomeDto map(SomeEntity entity);

    default IdToEntityConverter<Activity> activityConverter() {
        return new IdToEntityConverter<>(Activity.class);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:52:52