@Autowired 和 @Qualifier 详解
一、@Autowired 是什么
@Autowired是 Spring 提供的依赖注入注解,默认按类型(byType)自动装配。它告诉 Spring 容器:这个字段、构造方法或 Setter 方法需要一个依赖,请从容器中找一个匹配的 Bean 注入进来。
@ServicepublicclassUserService{@AutowiredprivateUserDaouserDao;}Spring 在创建UserService时,扫描到@Autowired标记的字段,从容器中查找UserDao类型的 Bean,通过反射赋值给该字段。
二、@Autowired 的三种注入位置
构造器注入
@ServicepublicclassUserService{privatefinalUserDaouserDao;publicUserService(UserDaouserDao){this.userDao=userDao;}}如果类只有一个构造方法,@Autowired可以省略。Spring 4.3 之后默认使用唯一的构造方法进行注入。
多个构造方法时必须显式标注:
@ServicepublicclassUserService{privatefinalUserDaouserDao;privatefinalEmailServiceemailService;@AutowiredpublicUserService(UserDaouserDao,EmailServiceemailService){this.userDao=userDao;this.emailService=emailService;}publicUserService(UserDaouserDao){this(userDao,null);}}字段注入
@ServicepublicclassUserService{@AutowiredprivateUserDaouserDao;}代码最简洁,但不利于单元测试,也无法使用final修饰字段。
Setter 注入
@ServicepublicclassUserService{privateUserDaouserDao;@AutowiredpublicvoidsetUserDao(UserDaouserDao){this.userDao=userDao;}}适合依赖可选的场景,可以在对象创建后动态替换依赖。
三、@Autowired 的匹配规则
第一步:按类型查找
Spring 首先在容器中查找与目标类型匹配的 Bean。如果只找到一个,直接注入。
第二步:按名称匹配
如果找到多个同类型的 Bean,Spring 会用字段名或参数名去匹配 Bean 的名称。
@ServicepublicclassOrderService{@AutowiredprivatePaymentServicealiPayService;// 字段名 aliPayService}如果容器中有aliPayService和wechatPayService两个PaymentService类型的 Bean,字段名aliPayService会匹配到对应的 Bean。
第三步:@Primary 优先
如果按名称也没有匹配到,Spring 会查找标记了@Primary的 Bean。
@Service@PrimarypublicclassAliPayServiceimplementsPaymentService{}第四步:@Qualifier 指定
如果以上都无法确定,使用@Qualifier显式指定 Bean 名称。
@Autowired@Qualifier("aliPayService")privatePaymentServicepaymentService;匹配流程图
按类型查找 ├── 找到一个 → 注入 ├── 找到多个 │ ├── 按字段名/参数名匹配 Bean 名称 │ │ ├── 匹配成功 → 注入 │ │ └── 匹配失败 │ │ ├── 有 @Primary → 注入 @Primary 标记的 Bean │ │ └── 有 @Qualifier → 按 @Qualifier 指定的名称注入 │ │ └── 都没有 → 抛 NoUniqueBeanDefinitionException └── 没找到 ├── required = true(默认)→ 抛 NoSuchBeanDefinitionException └── required = false → 注入 null四、@Autowired 的 required 属性
@Autowired(required=false)privateUserDaouserDao;默认required = true,找不到匹配的 Bean 时启动报错。设置为false时,找不到则注入null,不报错。
required = false适合可选依赖的场景,但要注意使用前判空。
五、@Qualifier 的作用
@Qualifier配合@Autowired使用,在同类型多 Bean 时按名称指定具体注入哪一个。
@ServicepublicclassOrderService{@Autowired@Qualifier("aliPayService")privatePaymentServicepaymentService;}@Qualifier的值就是目标 Bean 的名称。Bean 名称默认是类名首字母小写,也可以显式指定:
@Service("aliPayService")publicclassAliPayServiceimplementsPaymentService{}六、@Qualifier 的使用位置
字段上
@Autowired@Qualifier("aliPayService")privatePaymentServicepaymentService;构造器参数上
@ServicepublicclassOrderService{privatefinalPaymentServicepaymentService;publicOrderService(@Qualifier("aliPayService")PaymentServicepaymentService){this.paymentService=paymentService;}}Setter 方法参数上
@AutowiredpublicvoidsetPaymentService(@Qualifier("aliPayService")PaymentServicepaymentService){this.paymentService=paymentService;}方法参数上(@Bean 方法)
@BeanpublicOrderServiceorderService(@Qualifier("aliPayService")PaymentServicepaymentService){returnnewOrderService(paymentService);}七、@Qualifier 与 @Primary 的优先级
当两者同时存在时,@Qualifier的优先级更高。
@Service@PrimarypublicclassAliPayServiceimplementsPaymentService{}@ServicepublicclassWechatPayServiceimplementsPaymentService{}@ServicepublicclassOrderService{@Autowired@Qualifier("wechatPayService")// 显式指定,覆盖 @PrimaryprivatePaymentServicepaymentService;// 注入 WechatPayService}@Qualifier是精确指定,@Primary是默认首选。精确指定优先于默认。
八、@Qualifier 用于集合注入
当注入集合类型时,@Qualifier可以筛选特定名称的 Bean:
@Autowired@Qualifier("aliPayService")privateList<PaymentService>paymentServices;但这只对按名称匹配的 Bean 有效。更常见的做法是给 Bean 加上自定义注解,然后用@Qualifier筛选。
九、自定义 @Qualifier 注解
Spring 支持通过自定义注解扩展@Qualifier的能力:
@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic@interfaceAliPay{}使用:
@Service@AliPaypublicclassAliPayServiceimplementsPaymentService{}@ServicepublicclassOrderService{@Autowired@AliPayprivatePaymentServicepaymentService;}这种方式比字符串名称更类型安全,重构时 IDE 能自动更新。
十、底层原理
处理类
@Autowired和@Qualifier由AutowiredAnnotationBeanPostProcessor处理。它实现了BeanPostProcessor接口,在 Bean 实例化后、初始化前介入。
处理时机
实例化 → 属性填充(populateBean) └── AutowiredAnnotationBeanPostProcessor.postProcessProperties() ├── 扫描字段和方法上的 @Autowired ├── 解析依赖类型 ├── DefaultListableBeanFactory.resolveDependency() │ ├── 按类型查找 │ ├── 按 @Qualifier 筛选 │ ├── 按 @Primary 选择 │ └── 按名称匹配 └── 反射注入核心方法
// AutowiredAnnotationBeanPostProcessorpublicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){// 1. 查找注入元数据InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs);// 2. 执行注入metadata.inject(bean,beanName,pvs);returnpvs;}// DefaultListableBeanFactorypublicObjectresolveDependency(DependencyDescriptordescriptor,StringrequestingBeanName,Set<String>autowiredBeanNames,TypeConvertertypeConverter){// 1. 如果字段类型是 Optional、ObjectProvider 等,特殊处理// 2. 查找匹配的 BeanObjectresult=doResolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter);returnresult;}doResolveDependency()中会检查@Qualifier注解的值,如果有,就按名称匹配;如果没有,走类型匹配逻辑。
十一、@Autowired vs @Resource
| 对比维度 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring | JSR-250(JDK) |
| 默认匹配 | 按类型 | 按名称 |
| 指定名称 | @Qualifier | name属性 |
| required 属性 | 支持 | 不支持 |
| 构造器注入 | 支持 | 不支持 |
| 处理类 | AutowiredAnnotationBeanPostProcessor | CommonAnnotationBeanPostProcessor |
// @Autowired + @Qualifier@Autowired@Qualifier("userDaoImpl")privateUserDaouserDao;// @Resource@Resource(name="userDaoImpl")privateUserDaouserDao;两者功能等价,@Resource写起来更简洁,但@Autowired功能更丰富(支持构造器注入、required = false)。
十二、常见问题
1. NoUniqueBeanDefinitionException
NoUniqueBeanDefinitionException: expected single matching bean but found 2容器中有多个同类型 Bean,且无法按名称匹配。解决方案:
- 用
@Qualifier指定名称 - 用
@Primary标记首选 - 将字段名改为与目标 Bean 名称一致
2. NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No qualifying bean of type 'UserDao' available容器中没有匹配的 Bean。检查:
- 目标类是否被 Spring 管理
- 包路径是否在
@ComponentScan范围内 - 是否设置了
required = false
3. 构造器注入循环依赖
构造器注入无法解决循环依赖,启动报BeanCurrentlyInCreationException。应通过重构消除循环依赖,或改用字段注入。
4. @Qualifier 值写错
@Qualifier的值必须与 Bean 名称完全一致(大小写敏感)。Bean 默认名称是类名首字母小写,显式指定时以指定值为准。
5. 自定义 @Qualifier 注解不生效
检查自定义注解是否标注了@Qualifier元注解,且@Retention为RUNTIME。
十三、最佳实践
优先使用构造器注入。保证依赖不可变、完全就绪,便于单元测试。
同类型多 Bean 时优先用自定义 @Qualifier 注解。比字符串名称更类型安全,重构友好。
@Qualifier 的粒度要细。一个@Qualifier只对应一个 Bean,不要一个名称对应多个。
避免字段注入。不利于测试,容易积累过多依赖而不自知。
@Primary 用于设置合理的默认值。当大多数场景都用某个实现时,标记它为@Primary,特殊情况再用@Qualifier指定。
@Qualifier 与 @Primary 配合使用。@Primary提供默认,@Qualifier处理例外。
十四、总结
| 维度 | @Autowired | @Qualifier |
|---|---|---|
| 作用 | 标记需要注入的依赖 | 指定注入的具体 Bean |
| 匹配方式 | 先按类型,再按名称 | 按名称 |
| 单独使用 | 可以 | 通常配合 @Autowired |
| 优先级 | 低于 @Qualifier | 高于 @Primary |
| 支持位置 | 字段、构造器、Setter、参数 | 字段、构造器参数、Setter 参数、方法参数 |
| 处理类 | AutowiredAnnotationBeanPostProcessor | 同上 |
@Autowired负责“需要一个依赖”,@Qualifier负责“要哪一个”。当容器中只有一个匹配类型的 Bean 时,@Autowired就能完成注入;当有多个时,@Qualifier用来消除歧义。两者配合使用,是 Spring 依赖注入中最常见的组合。理解它们的匹配规则和优先级,能帮你快速定位注入异常,写出更清晰的依赖关系。f