Spring 容器注入时查找 Bean 的完整规则

Spring 容器注入时查找 Bean 的完整规则

彻底搞懂 Spring IoC 在运行时到底“先找谁、再找谁、如何决策”,一文掌握源码级细节。


一、为什么要谈“查找规则”?

在 Spring 应用中,我们最常见的代码是:

@Autowired
private OrderService orderService;

容器启动后,Spring 必须回答两个问题:

  1. 有哪些候选 Bean?
  2. 最终注入哪一个?

这两个问题的答案,就是 Spring 容器注入时查找 Bean 的完整规则


二、整体流程(源码视角)

AbstractBeanFactory#doGetBean
  └── DefaultListableBeanFactory#resolveDependency
        ├── 1️⃣ 按类型找候选   (findAutowireCandidates)
        ├── 2️⃣ 过滤歧义       (QualifierAnnotationAutowireCandidateResolver)
        ├── 3️⃣ 确定唯一       (determineAutowireCandidate)
        └── 4️⃣ 创建或返回     (getBean → createBean)

三、四步查找规则详解

① 按类型找候选(findAutowireCandidates)

  • 输入:接口/类 Class requiredType
  • 输出Map —— 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 BeanDefinitionReader id 或类名
spring.factories ImportSelector 全限定类名
手动注册 BeanDefinitionRegistry 显式指定

五、歧义消除的 4 种官方姿势

方式 示例 优先级
@Qualifier @Qualifier("stripe") 最高
变量名匹配 PaymentService stripe; 次高
@Primary @Primary @Service 兜底
List/Map List 全部注入

六、一张脑图速记

注入点 @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 秒内定位。

你可能感兴趣的:(SpringBoot,高效的Java开发实践,spring,java,后端,spring,boot)