报
NoSuchBeanDefinitionException: No qualifying bean of type ... available十有八九不是代码写错,是 Spring 压根没扫到这个类。Spring Boot 默认只扫描主类所在包和它的子包,Bean 定义在别的包、或少了@Service这类注解,容器里就没有它。
Issue & Env(案发现场与环境)
- 业务场景:分模块或多包工程里,
UserService定义在一个包,UserController在另一个包,注入时启动失败;或 Bean 类漏写@Service/@Component。 - 环境:Spring Boot 2.x / 3.x 通用。默认组件扫描行为由
@SpringBootApplication里的@ComponentScan决定。
StackTrace(堆栈日志)
典型的注入失败堆栈,UnsatisfiedDependencyException 包着 NoSuchBeanDefinitionException:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController' defined in file [/Users/dev/demo/target/classes/com/example/demo/UserController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1388)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:612)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:962)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:930)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:596)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1296)
at com.example.demo.Application.main(Application.java:12)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:2126)
at org.springframework.beans.factory.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:2093)
at org.springframework.beans.factory.DefaultListableBeanFactory.doResolveBeanForType(DefaultListableBeanFactory.java:1747)
at org.springframework.beans.factory.DefaultListableBeanFactory.doResolveMultipleBeans(DefaultListableBeanFactory.java:1383)
at org.springframework.beans.factory.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1313)
at org.springframework.beans.factory.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1575)
at org.springframework.beans.factory.DefaultListableBeanFactory.doResolveMultipleBeans(DefaultListableBeanFactory.java:1351)
at org.springframework.beans.factory.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1313)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AutowireCapableBeanFactory.java:738)
... 19 more
Reproduce(复现代码)
主类放在 com.example.demo,Service 却放在平级之外的 com.example.service:
// com/example/demo/Application.java
package com.example.demo;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// com/example/service/UserService.java —— 在 com.example.demo 之外
package com.example.service;
@Service
public class UserService {
public void hello() {}
}
// com/example/demo/UserController.java
package com.example.demo;
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
}
启动即抛上面那段 NoSuchBeanDefinitionException。UserService 有 @Service 注解,但它在默认扫描路径之外,容器没把它登记为 Bean。
Root Cause(根因分析)
@SpringBootApplication 是一个组合注解,其中包含 @ComponentScan。@ComponentScan 没有显式指定 basePackages 时,扫描起点是声明这个注解的类所在的包——也就是主类 Application 所在的 com.example.demo,以及它的所有子包。
com.example.service 和 com.example.demo 是平级兄弟包,既不是子包也不包含主类包,自然不在扫描范围内。于是 UserService 有注解也不会被 ClassPathScanningCandidateComponentProvider 找到,容器里没有「类型为 UserService 的 Bean」,注入时报 NoSuchBeanDefinitionException。
另一个高频变体是注解漏写:类没有 @Service / @Component / @Repository,即使在同一包内也不会被扫描登记。
还有一个更容易翻车的写法——把 @ComponentScan 的范围写错。spring-boot issue #16063 里,有人在主类上写 @ComponentScan("*.*"),扫描范围直接扩到整个依赖 jar,连 spring-boot-autoconfigure 里的自动配置类都被扫进去,报的是 BeanDefinitionStoreException。根子是同一个:@ComponentScan 的边界没搞清。
The Fix(解决方案)
首选:把 Bean 挪到主类包下面
最省事也最干净,让目录跟随主类包:
com.example.demo
├── Application.java
├── controller
│ └── UserController.java
└── service
└── UserService.java
service 变成 com.example.demo 的子包,默认扫描能覆盖,无需任何额外配置。
跨包场景:显式声明扫描范围
Bean 在 Maven/Gradle 多模块或第三方包里,就主动扩大扫描范围:
@SpringBootApplication(scanBasePackages = {
"com.example.demo",
"com.example.service"
})
public class Application {}
或单独标注:
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.demo", "com.example.service"})
public class Application {}
注解漏写:补上 stereotype
确认类里有对应注解:
@Service
public class UserService { ... }
Mapper 接口这类非 @Component 的 Bean,配合 @MapperScan("com.example.demo.mapper") 显式指定扫描包,别指望默认组件扫描。
