今天带你了解一下 Spring 框架中的 @Qualifier
注解,它解决了哪些问题,以及如何使用它。我们还将了解它与 @Primary
注解的不同之处。
使用 @Autowired
注解是 Spring 依赖注入的绝好方法。但是有些场景下仅仅靠这个注解不足以让Spring知道到底要注入哪个 bean。默认情况下,@Autowired
按类型装配 Spring Bean。如果容器中有多个相同类型的 bean,则框架将抛出 NoUniqueBeanDefinitionException
, 以提示有多个满足条件的 bean 进行自动装配。程序无法正确做出判断使用哪一个,下面就是个鲜活的例子:
@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;
//todo
}
如果我们尝试将 FooService
加载到我们的上下文中,Spring 框架将抛出 NoUniqueBeanDefinitionException
。这是因为 Spring 不知道要注入哪个 bean。为了避免这个问题,有几种解决方案。那么我们本文要讲解的 @Qualifier
注解就是其中之一。
通过使用 @Qualifier
注解,我们可以消除需要注入哪个 bean 的问题。让我们重新回顾一下前面的例子,看看我们如何通过包含 @Qualifier
注释来指出我们想要使用哪个 bean 来解决问题:
@Component
public class FooService {
@Autowired
@Qualifier("fooFormatter")
private Formatter formatter;
//todo
}
通过将 @Qualifier
注解与我们想要使用的特定 Spring bean 的名称一起进行装配,Spring 框架就能从多个相同类型并满足装配要求的 bean 中找到我们想要的,避免让Spring脑裂。我们需要做的是@Component或者@Bean注解中声明的value属性以确定名称。其实我们也可以在 Formatter
实现类上使用 @Qualifier
注释,而不是在 @Component
或者 @Bean
中指定名称,也能达到相同的效果:
@Component
// 指明bean名称
@Qualifier("fooFormatter")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@Component
// 指明bean名称
@Qualifier("barFormatter")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
@Component
public class FooService {
@Autowired
// 同样使用@Qualifier进行指明注入
@Qualifier("fooFormatter")
private Formatter formatter;
//todo
}
还有另一个名为 @Primary
的注解,我们也可以用来发生依赖注入的歧义时决定要注入哪个 bean。当存在多个相同类型的 bean 时,此注解定义了首选项。除非另有说明,否则将使用与 @Primary
注释关联的 bean 。
我们来看一个例子:
@Bean
public Employee tomEmployee() {
return new Employee("Tom");
}
@Bean
@Primary
public Employee johnEmployee() {
return new Employee("john");
}
@Component
public class Job1 {
// 注入的是john Employee,因为它有@Primary注解
@Autowired
private Employee employee;
// TODO
}
在此示例中,两个方法都返回相同的 Employee
类型。Spring 将注入的 bean 是方法 john Employee
返回的 bean。这是因为它包含 @Primary
注解。当我们想要指定默认情况下应该注入特定类型的 bean 时,此注解很有用。如果我们在某个注入点需要另一个 bean,我们需要专门指出它。我们可以通过 @Qualifier
注解来做到这一点。例如,我们可以通过使用 @Qualifier
注释来指定我们想要使用 tomEmployee
方法返回的 bean 。值得注意的是,如果 @Qualifier
和 @Primary
注释都存在,那么 @Qualifier
注释将具有优先权。基本上,@Primary
是定义了默认值,而 @Qualifier
则非常具体。当然 @Component
也可以使用 @Primary
注解,这次使用的还是上面3的示例:
@Component
@Primary
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@Component
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
@Component
public class Job1 {
// 注入的是FooFormatter,因为它有@Primary注解
@Autowired
private Formatter formatter;
// TODO
}
在这种情况下,@Primary
注解指定了默认注入的是 FooFormatter
,消除了场景中的注入歧义。
在使用 @Autowired
进行自动装配时,如果 Spring 没有其他提示,将会按照需要注入的变量名称来寻找合适的 bean。也可以解决依赖注入歧义的问题。让我们看一些基于我们最初的例子的代码:
@Component
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@Component
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
@Component
public class Job1 {
// 虽然没用使用@Primary,但注入的是FooFormatter,因为变量名就是beanName
// beanName默认就是类名的驼峰命名
@Autowired
private Formatter fooFormatter;
// TODO
}
在这种情况下,Spring 将确定要注入的 bean 是 FooFormatter
,因为字段名称与我们在该 bean 的 @Component
或者 @Bean
注解中使用的值(默认 @Bean
使用方法名)相匹配。
通过对 @Qualifier
的探讨,我们知道该注解是用来消除依赖注入冲突的。这种可以解决在日常开发中,经常遇到的一个接口多个实现类的注入问题。这将有助于你对 Spring 的依赖注入机制的了解。
原文地址:https://zhuanlan.zhihu.com/p/100371910?from_voters_page=true