bigywater.tech
Spring · 5

依赖注入:构造器、setter、字段注入,@Autowired 的候选解析与 @Primary、@Qualifier

@Autowired 到底按什么找 Bean?两个同类型的 Bean 怎么选?构造器注入、setter 注入、字段注入有什么不同?没有候选时会怎样?这篇在 Spring Framework 7.0.9 上把各种写法和候选解析的结果逐个跑出来。

4 种
能在两个同类型候选里选出一个的办法:参数名、@Qualifier、@Primary、@Priority
size=0
参数是 List 而没有任何 Pay Bean 时,拿到的列表大小(不报错)
null
脱离容器直接 new 一个字段注入的类时,字段的值

三种注入方式

实测 · 本机执行输出
  容器里:构造器注入 d 非 null;setter 注入 d 非 null;字段注入 d 非 null
  脱离容器直接 new:new Setter() → d=null;new Field() → d=null;Ctor 没有无参构造器,必须把 Dep 传进去才能 new,所以不会出现“忘了注入”

构造器注入强制传参,setter 与字段注入 new 出来是 null;容器里三种都能注入。

多个候选

实测 · 本机执行输出
  (a) 两个 Pay,按类型注入,没有任何提示:
    NoHint → NoUniqueBeanDefinitionException: No qualifying bean of type 'Pay' available: expected single matching bean but found 2: alipay,wechat
  (b) 参数名等于 Bean 名(需要 javac -parameters,本实验已加):
    参数名 alipay → alipay
  (c) @Qualifier:
    @Qualifier("wechat") → wechat
  (d) List<Pay> 与 Map<String,Pay>:
    List<Pay> → [alipay, wechat];Map<String,Pay> 的键 → [alipay, wechat]
  (e) @Primary:
    @Primary(wechat 标了) → wechat
  (f) 两个都是 @Primary:
    TwoPrimary → NoUniqueBeanDefinitionException: No qualifying bean of type 'Pay' available: more than one 'primary' bean found among candidates: [alipay, wechat]
  (g) @Order(2) 的 alipay 与 @Order(1) 的 wechat:
    List<Pay> → [wechat, alipay];Map<String,Pay> 的键 → [alipay, wechat]
  (h) @Priority:
    @Priority(1) 的 alipay vs @Priority(5) 的 wechat,按类型注入 → alipay

按类型找到多个时失败,参数名、@Qualifier、@Primary、@Priority 能选出一个,两个 @Primary 仍冲突。

没有候选

实测 · 本机执行输出
  (a) 构造器参数 Pay,没有任何候选:
    NeedsPay → NoSuchBeanDefinitionException: No qualifying bean of type 'Pay' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
  (b) 参数 List<Pay>,没有任何候选:
    List<Pay> 没有候选 → size=0
  (c) Optional / ObjectProvider / required=false:
    Optional<Pay> → isPresent=false;ObjectProvider.getIfAvailable → null
    字段 p=null

必需的单个依赖直接失败,集合依赖是空集合,可选依赖可以写成 Optional、ObjectProvider 或 required=false。

泛型

实测 · 本机执行输出
    Repo<Integer> → Integer 仓库;Repo<String> → String 仓库

泛型参数参与候选匹配,不需要 @Qualifier。

分步演示

依赖注入与候选解析
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9;javac 加了 -parameters;不加这个参数时按参数名匹配 Bean 名的结果,这篇没有验证
    • 没有验证:字段注入下按字段名匹配;@Resource 与 @Inject 的解析顺序;@Qualifier 作为自定义注解;循环依赖(见下一篇);@Autowired 在 Boot 的组件扫描下的行为;Kotlin 或 Lombok 生成的构造器
    京ICP备2026067502号-1京公网安备11010502063487号