无法拆分过大类求助:含26个Mapper参数的构造器优化方案
优化AssignedSelectMapper构造器参数过多的方案
针对你遇到的构造器参数过多(26个Mapper)且无法通过MapStruct自动处理的问题,我分享几个在实际项目中验证过的优化方向:
方案1:创建Mapper注册表统一管理子映射器
把所有子Mapper集中到一个专门的注册表类中,AssignedSelectMapper只依赖这个注册表,彻底减少构造器参数数量。
实现步骤:
- 定义Mapper注册表类,集中注入所有子Mapper:
@Component public class SelectionMapperRegistry { private final AssignedOpDtoMapper opMapper; private final AssignedOrderDtoMapper orderMapper; // 其他24个Mapper... // 这里虽然参数多,但该类的核心职责就是集中管理映射器,属于合理的特殊场景 public SelectionMapperRegistry(AssignedOpDtoMapper opMapper, AssignedOrderDtoMapper orderMapper, /* ... */) { this.opMapper = opMapper; this.orderMapper = orderMapper; // 初始化所有Mapper引用 } // 提供获取各Mapper的方法 public AssignedOpDtoMapper getOpMapper() { return opMapper; } public AssignedOrderDtoMapper getOrderMapper() { return orderMapper; } // 其他Mapper的get方法... }
- 修改AssignedSelectMapper,仅依赖注册表:
@Component public class AssignedSelectMapper { private final SelectionMapperRegistry mapperRegistry; // 构造器现在仅1个参数,彻底解决SonarLint警告 public AssignedSelectMapper(SelectionMapperRegistry mapperRegistry) { this.mapperRegistry = mapperRegistry; } // 正向映射示例 private List<AssignedSelectionCriteria> mapOps(List<OptionCodeDto> optionCodeDtos) { return mapperRegistry.getOpMapper().mapCriterias(optionCodeDtos); } // 反向映射示例 private void setOptionCodes(SelectionDto selectionDto, List<AssignedSelectionCriteria> assignedSelectionCriterias) { List<OptionCodeDto> optionCodeDtos = mapperRegistry.getOpMapper().mapAssignedCriterias(assignedSelectionCriterias); selectionDto.setOptionCodes(optionCodeDtos); } }
优点:彻底解决构造器参数冗余问题,所有Mapper引用集中管理,后续新增Mapper仅需修改注册表类。
缺点:注册表类本身会有较多参数,但可通过// NOSONAR注释忽略SonarLint警告(注明该类为特殊场景)。
方案2:利用Spring ApplicationContext动态获取Mapper(Spring项目适用)
如果你的项目基于Spring,可以直接通过容器上下文按类型获取子Mapper,无需在构造器中注入所有依赖。
实现示例:
@Component public class AssignedSelectMapper implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } // 正向映射示例 private List<AssignedSelectionCriteria> mapOps(List<OptionCodeDto> optionCodeDtos) { AssignedOpDtoMapper mapper = applicationContext.getBean(AssignedOpDtoMapper.class); return mapper.mapCriterias(optionCodeDtos); } // 反向映射示例 private void setOptionCodes(SelectionDto selectionDto, List<AssignedSelectionCriteria> assignedSelectionCriterias) { AssignedOpDtoMapper mapper = applicationContext.getBean(AssignedOpDtoMapper.class); List<OptionCodeDto> optionCodeDtos = mapper.mapAssignedCriterias(assignedSelectionCriterias); selectionDto.setOptionCodes(optionCodeDtos); } }
优点:构造器完全无参数,无需维护大量注入依赖,新增Mapper时无需修改AssignedSelectMapper。
缺点:代码耦合Spring容器;每次获取Mapper会有极轻微的性能开销(Spring Bean为单例,可忽略)。
方案3:策略模式重构映射逻辑
虽然所有Dto没有共同父类,但可以为每种映射逻辑创建独立的策略类,将策略按类型/标识注册到Map中,AssignedSelectMapper通过Map调用对应策略。
实现步骤:
- 定义映射策略接口(区分正向/反向):
// 正向映射策略接口 public interface ForwardMappingStrategy<T> { List<AssignedSelectionCriteria> map(List<T> dtoList); Class<T> getDtoType(); // 显式声明对应Dto类型,避免反射风险 } // 反向映射策略接口 public interface ReverseMappingStrategy { void setDto(SelectionDto selectionDto, List<AssignedSelectionCriteria> criterias); String getIdentifier(); // 自定义标识,比如"ops"、"ora" }
- 为每种Dto实现对应的策略类:
@Component public class OpForwardStrategy implements ForwardMappingStrategy<OptionCodeDto> { private final AssignedOpDtoMapper mapper; public OpForwardStrategy(AssignedOpDtoMapper mapper) { this.mapper = mapper; } @Override public List<AssignedSelectionCriteria> map(List<OptionCodeDto> dtoList) { return mapper.mapCriterias(dtoList); } @Override public Class<OptionCodeDto> getDtoType() { return OptionCodeDto.class; } } @Component public class OpReverseStrategy implements ReverseMappingStrategy { private final AssignedOpDtoMapper mapper; public OpReverseStrategy(AssignedOpDtoMapper mapper) { this.mapper = mapper; } @Override public void setDto(SelectionDto selectionDto, List<AssignedSelectionCriteria> criterias) { List<OptionCodeDto> dtos = mapper.mapAssignedCriterias(criterias); selectionDto.setOptionCodes(dtos); } @Override public String getIdentifier() { return "ops"; } }
- 在AssignedSelectMapper中注入所有策略并分组:
@Component public class AssignedSelectMapper { private final Map<Class<?>, ForwardMappingStrategy<?>> forwardStrategyMap; private final Map<String, ReverseMappingStrategy> reverseStrategyMap; // Spring会自动将同类型的Bean注入到集合中 public AssignedSelectMapper(List<ForwardMappingStrategy<?>> forwardStrategies, List<ReverseMappingStrategy> reverseStrategies) { this.forwardStrategyMap = forwardStrategies.stream() .collect(Collectors.toMap(ForwardMappingStrategy::getDtoType, Function.identity())); this.reverseStrategyMap = reverseStrategies.stream() .collect(Collectors.toMap(ReverseMappingStrategy::getIdentifier, Function.identity())); } // 优化后的正向映射方法 public List<AssignedSelect> assignSelectFrom(SelectDto selectDto) { Objects.requireNonNull(selectDto, "selectionDto can not be NULL"); List<AssignedSelect> assignedSelects = new ArrayList<>(); assignedSelects.addAll(mapForward(selectDto.getOps(), OptionCodeDto.class)); assignedSelects.addAll(mapForward(selectDto.getOra(), OraDto.class)); // 其他类型... return assignedSelects; } @SuppressWarnings("unchecked") private <T> List<AssignedSelectionCriteria> mapForward(List<T> dtoList, Class<T> dtoType) { ForwardMappingStrategy<T> strategy = (ForwardMappingStrategy<T>) forwardStrategyMap.get(dtoType); if (strategy == null) { throw new IllegalArgumentException("No forward strategy found for type: " + dtoType.getName()); } return strategy.map(dtoList); } // 反向映射方法类似,通过标识调用对应策略 }
优点:扩展性极强,新增Dto类型仅需新增策略类,无需修改核心映射类;符合单一职责原则,每个策略仅处理一种映射逻辑。
缺点:初期重构工作量较大,需要定义多个策略类。
内容的提问来源于stack exchange,提问作者cmlonder
相关产品推荐
相关产品推荐

