Spring Boot中Mapper无法被Autowired注入的问题排查
博彩API开发中MapStruct注入问题解决
问题场景
开发博彩API时,使用MapStruct的BetMapper减少DTO与实体类之间的转换代码重复,但遇到以下问题:
- 项目启动失败,提示找不到
BetMapper类型的Bean - 移除
@Autowired注解后项目能启动,但调用投注插入接口时出现betMapper为null的空指针异常
启动报错信息
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 1 of constructor in api.loteria.loteriaapi.services.Mysql.BetServiceMysql required a bean of type 'api.loteria.loteriaapi.dtos.mappers.BetMapper' that could not be found. The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider defining a bean of type 'api.loteria.loteriaapi.dtos.mappers.BetMapper' in your configuration.
相关代码
BetMapper.java
@Mapper(componentModel = "spring") public interface BetMapper { @Mapping(target = "bet.id", source = "betId") Bet betResquetToEntity(BetRequest betRequest); @Mapping(source = "bet.id", target = "betId") BetResponse entityToBetResponse(Bet bet); }
BetServiceMysql.java
@Service @AllArgsConstructor(onConstructor = @__(@Autowired)) public class BetServiceMysql implements BetService { private BetRepository betRepository; private BetMapper betMapper; @Override public List<BetResponse> list() { List<Bet> bets = betRepository.findAll(); return bets.stream().map(bet -> betMapper.entityToBetResponse(bet)).collect(Collectors.toList()); } @Override public BetResponse save(BetRequest betRequest) { Bet bet = betMapper.betResquetToEntity(betRequest); try { betRepository.save(bet); }catch(RuntimeException e){ throw new DataIntegrityViolationException(e.getMessage()); } return betMapper.entityToBetResponse(bet); } @Override public BetResponse update(Long id, BetRequest betRequest) { Bet bet = verifyIfExist(id); updateData(bet, betRequest); betRepository.save(bet); return betMapper.entityToBetResponse(bet); } @Override public BetResponse delete(Long id) { Bet bet = verifyIfExist(id); betRepository.delete(bet); return betMapper.entityToBetResponse(bet); } @Override public BetResponse getBetById(Long id) { // TODO Auto-generated method stub return null; } @Override public List<BetResponse> getBets() { // TODO Auto-generated method stub return null; } protected Bet verifyIfExist(Long id){ return betRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(String.format("ID: %s || Não foi encontrado nenhuma entidade para o id fornecido", id))); } protected void updateData(Bet bet, BetRequest betRequest){ bet.setMaxNumbersByUsers(betRequest.getMaxNumbersByUsers()); } }
BetService.java
public interface BetService { List<BetResponse> list(); BetResponse save(BetRequest betRequest); BetResponse update(Long id, BetRequest betRequest); BetResponse delete(Long id); BetResponse getBetById(Long id); List<BetResponse> getBets(); }
解决方案
1. 确保MapStruct依赖及注解处理器配置正确
MapStruct需要核心依赖和注解处理器才能生成Mapper的实现类,以Maven为例,在pom.xml中添加:
<!-- MapStruct核心依赖 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <!-- 注解处理器,用于编译时生成Mapper实现类 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency>
如果使用Gradle,需配置annotationProcessor:
dependencies { implementation 'org.mapstruct:mapstruct:1.5.5.Final' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' }
2. 确认Spring能扫描到Mapper接口
- 检查Spring Boot主类的
@SpringBootApplication或@ComponentScan注解,确保Mapper所在的包api.loteria.loteriaapi.dtos.mappers被包含在扫描范围内:
@SpringBootApplication(scanBasePackages = {"api.loteria.loteriaapi"}) public class LoteriaApiApplication { public static void main(String[] args) { SpringApplication.run(LoteriaApiApplication.class, args); } }
@Mapper(componentModel = "spring")注解会让MapStruct生成带有@Component的实现类,确保Spring能自动扫描并注册为Bean。
3. 优化Service的依赖注入方式
替换@AllArgsConstructor(onConstructor = @__(@Autowired))为更简洁的构造注入方式,推荐使用Lombok的@RequiredArgsConstructor(需确保字段为private final):
@Service @RequiredArgsConstructor public class BetServiceMysql implements BetService { private final BetRepository betRepository; private final BetMapper betMapper; // 原有业务方法保持不变... }
如果不使用Lombok,可手动编写构造函数:
@Service public class BetServiceMysql implements BetService { private final BetRepository betRepository; private final BetMapper betMapper; public BetServiceMysql(BetRepository betRepository, BetMapper betMapper) { this.betRepository = betRepository; this.betMapper = betMapper; } // 原有业务方法保持不变... }
4. 验证Mapper实现类是否生成
编译项目后,检查target/classes/api/loteria/loteriaapi/dtos/mappers/目录下是否存在BetMapperImpl.class文件。如果不存在,说明注解处理器未生效,需重新检查依赖配置,或执行mvn clean compile(Maven)/gradle clean compileJava(Gradle)重新编译。
总结
问题核心是MapStruct未生成有效的Spring Bean实现类,或Spring无法扫描到该Bean。通过完善依赖配置、确保组件扫描范围正确、优化注入方式,即可解决启动报错和Mapper为null的问题。
内容的提问来源于stack exchange,提问作者Wesquel
相关产品推荐
相关产品推荐

