解释自动装配的歧义性,如下:
public interface Dessert {
}
@Component
public class Cake implements Dessert{
}
@Component
public class Cookies implements Dessert{
}
@Component
public class IceCream implements Dessert{
}
@Configuration
@ComponentScan
public class TestConfig {
private Dessert dessert;
@Autowired
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
}
本例中,Dessert是要一个接口,并且有三个实现类:Cake、Cookies、IceCream。因这三个实现类都使用了@Component注解,在主键扫描的时候,能够发现它们并将它们创建为Sping上下文里的bean,然后Spring在自动装配setDessert()中的Dessert参数时,由于没有唯一、无歧义的可选值,Spring会抛出异常:
Parameter 0 of method setDessert in com.example.springboot.config.TestConfig required a single bean, but 3 were found:
- cake: defined in file [Cake.class]
- cookies: defined in file [Cookies.class]
- iceCream: defined in file [IceCream.class]
怎么解决如上歧义性问题呢?Spring提供的了众多解决方案,接下来我们使用设置首选bean(primary)或者使用限定符(qualifier)。
当你都喜欢上述Cake、Cookies、IceCream这三个甜点时,选择一个你最喜欢的那个,加上@Primary注解来标示你最喜欢它。在声明bean的时候,通过@Primary注解来设置为首选,告诉Spring,当自动装配遇到歧义性的时候,Spring将会使用首选的bean。
代码如下:
@Component
@Primary
public class IceCream implements Dessert {
}
代码如下:
@Bean
@Primary
public Dessert iceCream(){
return new IceCream();
}
综上,在多个bean中选取一个bean成为首选bean。可是如果有多个bean都被加上了@Primary注解,在有多个首选bean的设置时,Spring也会无法选择装配哪个bean了!此时,可以为了解决这种歧义性,可以使用限定符(qualifier)。
@Qualifier注解是使用限定符的主要方式,它可以与@Autowired和@Inject协同使用,在注入的时候指定想要注入进去的那个bean。
例如,我们要确保将IceCream注入到setDessert()之中,代码如下:
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
@Qualifier(“iceCream”)指向的bean是组件扫描时所创建的bean,即IceCream的实例。
但是需要注意的是,限定符中指定的bean是与注入的bean的名称紧耦合的,对类名称的修改,都会导致限定符失效!
代码如下:
@Component
@Qualifier("cold")
public class IceCream implements Dessert {
}
限定符cold
分配给了IceCream bean,没有耦合类名,此时可以随意重构IceCream的类名,而不必担心破会自动装配。在注入时,使用cold
限定符就可以了:
@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
@Bean
@Qualifier("cold")
public Dessert iceCream(){
return new IceCream();
}
自定义一个注解来标示特定bean。
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
实现类上加上自定义注解:
@Component
@Cold
public class IceCream implements Dessert {
}
注入时,加上自定义注解
@Autowired
@Cold
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
@Bean
@Cold//bean声明cold限定符
public Dessert iceCream(){
return new IceCream();
}
@Autowired
@Cold//注入cold限定符指定的bean
public void setDessert(Dessert dessert){
this.dessert = dessert;
}