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
相关产品推荐
相关产品推荐

