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