最后更新:2026-10-28
适用场景:Spring Boot 3.x、构造器注入、NoSuchBeanDefinitionException

启动时报 required a bean of type ... that could not be found,看到类明明就在项目里,却注入不进来。先把“Java 类存在”和“Spring 容器里有这个 Bean”分开。编译器能找到类,只说明它在 classpath;Spring 还得扫描到、创建成功,并且类型匹配。

先读错误里真正缺的类型

不要只看最上面的 UnsatisfiedDependencyException。往下找 No qualifying bean of type '...' available,记下完整包名与注入点。比如缺的是 com.example.order.PayService,就去查这个接口的实现,而不是看到 OrderController 启动失败便去改 Controller。

一个常见的正常结构是:

com.example.app
├── DemoApplication.java     // @SpringBootApplication
├── controller
│   └── OrderController.java
└── service
    └── PayServiceImpl.java   // @Service

启动类放在业务包的上层,组件扫描才能覆盖子包。若启动类在 com.example.app.web,而 Service 在平级的 com.example.app.service,默认扫描范围就可能漏掉它。优先调整包结构;确需跨模块扫描时,再用明确的 scanBasePackageClasses,不要粗暴扫描整个 com。这是 Spring Boot 官方推荐的包布局。

类被扫到了吗?它是 Spring 管理的吗?

接口本身不会因为存在就自动成为 Bean。实现类要通过 @Service / @Component 等方式被扫描,或由配置类的 @Bean 方法返回。例如:

public interface PayService {
    void pay(long orderId);
}

@Service
public class PayServiceImpl implements PayService {
    @Override
    public void pay(long orderId) {
        // 支付逻辑
    }
}

两个类型分别放在自己的 Java 文件里。注入时优先用接口类型:

@RestController
public class OrderController {
    private final PayService payService;

    public OrderController(PayService payService) {
        this.payService = payService;
    }
}

只有一个构造器时,Spring 不要求再写 @Autowired。反过来,自己 new OrderController(...) 或 new PayServiceImpl() 得到的对象不由容器管理;这种情况通常表现为字段没注入或代理能力缺失,不要和“启动时找不到 Bean”混为一谈。Spring 的构造器注入文档有详细说明。

有注解仍然没有 Bean:查条件与环境

实现类若标了 @Profile("prod"),而当前跑的是 dev,它不会注册。@ConditionalOnProperty、@ConditionalOnClass 以及某些 Starter 的自动配置也会让 Bean 只在条件满足时出现。此时应查看当前激活的 profile、实际配置值和依赖树,不要单纯追加一个 @Component。

改完配置文件但只重启旧 Jar,也会让人误以为 Spring 忽略了注解。先确认部署的构建产物,再追条件配置。多环境配置可以对照Spring Boot 配置文件怎么写。

如果缺的是 MyBatis Mapper,检查 @Mapper / @MapperScan、Mapper 所在包和 MyBatis Starter;如果缺的是 Repository,还要看对应数据访问模块的启用范围。它们不是普通 @Service 实现类,处理方式要按各自框架来。Spring Boot 整合 MyBatis可以接着核对。

别把“找不到”和“找到太多”当成一回事

NoSuchBeanDefinitionException 是候选 Bean 不足;NoUniqueBeanDefinitionException 则是同一类型有多个候选。假如同时有 WechatPayService 和 AlipayPayService 实现 PayService,要根据业务选择 @Qualifier,或在确有默认实现时使用 @Primary。随手删掉一个实现、把注入改成 Object,只是把问题藏起来。Spring 的限定符说明给出了选择规则。

排查顺序很简单:先读完整缺失类型,再查启动类包位置、实现类注册方式、条件配置,最后看是不是多实现冲突。每改一处就重新启动一次,确认异常链里缺失的类型是否变化,别同时改扫描路径和注入方式,最后不知道哪步起了作用。

参考资料

Spring Boot 3.5:Structuring Your Code
Spring Framework 6.2:Using @Autowired
Spring Framework 6.2:Autowiring Qualifiers

更新记录

2026-10-28:创建文章,补充包扫描、条件注册与多实现的分流排查。