Spring 容器注入时查找 Bean 的完整规则
Spring 容器注入时查找 Bean 的完整规则
彻底搞懂 Spring IoC 在运行时到底“先找谁、再找谁、如何决策”,一文掌握源码级细节。
一、为什么要谈“查找规则”?
在 Spring 应用中,我们最常见的代码是:
@Autowired
private OrderService orderService;
容器启动后,Spring 必须回答两个问题:
- 有哪些候选 Bean?
- 最终注入哪一个?
这两个问题的答案,就是 Spring 容器注入时查找 Bean 的完整规则。
二、整体流程(源码视角)
AbstractBeanFactory#doGetBean└── DefaultListableBeanFactory#resolveDependency├── 1️⃣ 按类型找候选 (findAutowireCandidates)├── 2️⃣ 过滤歧义 (QualifierAnnotationAutowireCandidateResolver)├── 3️⃣ 确定唯一 (determineAutowireCandidate)└── 4️⃣ 创建或返回 (getBean → createBean)
三、四步查找规则详解
① 按类型找候选(findAutowireCandidates)
- 输入:接口/类
Class<?> requiredType
- 输出:
Map<String, Object>
—— beanName → Bean 实例 - 规则:
- 递归 所有父容器(父子上下文场景)
- 包含 FactoryBean#getObjectType 返回的类型
- 过滤 抽象、非单例、非自动装配候选 (
isAutowireCandidate
)
② 过滤歧义(QualifierAnnotationAutowireCandidateResolver)
- @Qualifier 精确匹配
- @Primary 标记首选
- 自定义限定符注解(元注解
@Qualifier
) - 变量名与 beanName 匹配
③ 确定唯一(determineAutowireCandidate)
候选数 | 决策路径 | 结果 |
---|---|---|
0 | 抛 NoSuchBeanDefinitionException | 注入失败 |
1 | 直接返回唯一 Bean | 成功 |
>1 | 继续 ② 规则,直到只剩 1 个 | 成功 |
仍 >1 | 抛 NoUniqueBeanDefinitionException | 注入失败 |
④ 创建或返回(getBean)
- 单例已存在 → 直接返回
- 单例不存在 → 走 完整生命周期(实例化 → 依赖注入 → 初始化 → 暴露)
四、候选来源总览
来源 | 如何注册 | 默认 beanName |
---|---|---|
@Component 扫描 | 类路径扫描 | 首字母小写类名 |
@Bean 方法 | @Configuration 解析 | 方法名 |
XML <bean> | BeanDefinitionReader | id 或类名 |
spring.factories | ImportSelector | 全限定类名 |
手动注册 | BeanDefinitionRegistry | 显式指定 |
五、歧义消除的 4 种官方姿势
方式 | 示例 | 优先级 |
---|---|---|
@Qualifier | @Qualifier("stripe") | 最高 |
变量名匹配 | PaymentService stripe; | 次高 |
@Primary | @Primary @Service | 兜底 |
List/Map | List<PaymentService> | 全部注入 |
六、一张脑图速记
注入点 @Autowired↓
resolveDependency(requiredType)├─ 1️⃣ findAutowireCandidates()│ ├─ 扫描所有 BeanDefinition│ ├─ FactoryBean 代理类型│ └─ 过滤非候选├─ 2️⃣ checkQualifiers()│ ├─ @Qualifier│ ├─ @Primary│ └─ 变量名├─ 3️⃣ determineAutowireCandidate()│ ├─ 0 → NoSuch│ ├─ 1 → OK│ └─ >1 → NoUnique└─ 4️⃣ getBean()├─ 已存在单例 → 直接返回└─ 不存在 → 创建
七、一句话总结
Spring 容器注入时查找 Bean 的完整规则 就是:
“先按类型找出所有候选,再用 @Qualifier/@Primary/变量名 逐级过滤,最终只剩一个 Bean 才注入;否则抛异常。”
记住这 4 步,任何注入失败都能 30 秒内定位。