分类
spring

Spring @Qualifier 注解

The Spring @Qualifier Annotation

1. 概述

本文我们将共同学习@Qualifier注解的作用以及其使用方法。在此基础上,将与@Primary 注解做一个简单的对比。

2. 自动装配的唯一性要求

在Spring项目中 @Autowired 注解是完成依赖注入的方式之一。但有些时候,此注解会在应该注入谁的问题上犯嘀咕。

在默认情况下,Spring是通过类型来完成依赖注入的。

注入过程中如果当前容器中出现多个bean的类型相同,Spring框架则会抛出 NoUniqueBeanDefinitionException 异常,来告诉我们:由于要注入类型的Bean在当前容器中不唯一,所以Spring无法为我们做主此时应该注入哪个。

比如:

@Component("fooFormatter★")
public class FooFormatter implements Formatter ❶ {
 
    public String format() {
        return "foo";
    }
}
 
@Component("barFormatter★")
public class BarFormatter implements Formatter ❶{
 
    public String format() {
        return "bar";
    }
}
 
@Component
public class FooService {
     
    @Autowired ❷
    private Formatter formatter;
}
  • ★ 自定义bean的名字,将在后面用到它
  • ❶ 两个bean的类型均为Formatter
  • ❷ 自动注入时将发生NoUniqueBeanDefinitionException异常。

❷这是由于此时满足类型为FooService的Bean有两个(fooFormatter以及barFormatter),而Spring并不能确认我们的真实意图是注入哪一个。

解决上述问题有多种方案,使用@Qualifier注解的方式是解决方案之一。

3. @Qualifier 注解

 @Qualifier 注解将显式的告之Spring依赖注入时应该注入的具体bean。

比如:

public class FooService {
     
    @Autowired
    @Qualifier("fooFormatter❶")
    private Formatter formatter;
}
  • ❶ 该值为前面在定义Bean时@Component注解中的值

@Qualifier显式地指名此时注入的bean为fooFormatter,所以即使barFormatter的类型同样为Formatter,此时也不会面临Spring不知道应该注入谁的问题。

我们注意到❶中使用了名称"fooFormatter",这是由于我们在前面定义Bean时在@Component注解中使用了相同的名称"fooFormatter"。在实现自定义Bean的名称功能时,除了使用@Component注解以外,还可以使用@Qualifier注解,两者的作用相同。

@Component
@Qualifier("fooFormatter") ❶
public class FooFormatter implements Formatter {
    //...
}
 
@Component
@Qualifier("barFormatter") ❶
public class BarFormatter implements Formatter {
    //...
}
  • ❶ 在@Qualifier注解中自定义Bean的名称

4. @Qualifier 与 @Primary

除了@Qualifier以外,还有一个@Primary注解也拥有在依赖注入时消除歧义的功能。

Primary译为主要的、首要的,该注解的作用是:当某个声明注入的类型存在多个bean时,除非你显式地声明使用哪个bean,否则将使用那个以@Primary注解的首要Bean。

示例如下:

@Configuration
public class Config {
 
    @Bean
    public Employee johnEmployee() {
        return new Employee("John");
    }
 
    @Bean
    @Primary★
    public Employee❶ tonyEmployee() {
        return new Employee("Tony");
    }
}
  • ❶ 两个Bean的类型相同,均为Employee
  • ★ 此Bean被声明为:首要的、主要的。

此时,若未声明注入的Bean,则使用方法tonyEmployee()的返回值生成的Bean来完成注入:

public class FooService {
     
    @Autowired ★
    private Employee employee;
}
  • ★ 此时将注入以@Primary注解的方法tonyEmployee()的返回值生成的Bean。

注意:Bean是唯一的,多次注入Employee,仅仅会执行一次tonyEmployee()方法。

前面我们有介绍@Primary时,多次强调了:未显式声明时才生效。也就是说一旦在注入时,显式地声明了注入的Bean名称则@Primary将失效。也就是说,在优先级上做比较:@Qualifier("xx") 高于 @Primary

比如:

@Component("fooFormatter")
@Primary ❶
public class FooFormatter implements Formatter {
 
    public String format() {
        return "foo";
    }
}
 
@Component("barFormatter")
public class BarFormatter implements Formatter {
 
    public String format() {
        return "bar";
    }
}

@Component
public class FooService {
     
    @Autowired
    @Qualifier("barFormatter") ★
    private Formatter formatter1;
}
  • ❶ 被声明为Primary
  • ★ 此时注入的为barFormatter

5.  @Qualifier与按名称自动装配

还有一种解决冲突的方法是在@Authwired注解的字段上,使用一个与Bean名称相同的字段名,这也是在未显式声明依赖bean的情况下Spring使用的默认策略。

比如:

public class FooService {
     
    @Autowired
    private Formatter barFormatter★;
}
  • ★ 字段名barFormatter与Bean名相同,此时将忽略@Primary注解,而注入的为barFormatter

但当显式的声明bean时,Spring则会以以显示声明的为准:

    @Autowired
    @Qualifier("barFormatter") ★
    private Formatter fooFormatter❶;
  • ❶ 由于显式声明的存在,所以字段名匹配原则将失败
  • ★ 将按显式声明的bean名称进行自动装配

6. 总结

在依赖注入过程中Spring默认使用按类型进行匹配的装配原则,当存在多个bean均满足条件时,spring将优先查找是显式声明的bean,如果未显示声明bean则将按字段名称进行查找,如果字段名称未查找成功则将继续查找使用了@Primary注解的bean,以上方法均未查找成功,则将抛出NoUniqueBeanDefinitionException异常。在装配bean的优先级上:@Qualifier > 按字段名称匹配 > @Primary