@Autowired
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
我们使用@Autowired注解标注了setDessert()方法,而他依赖Dessert接口,但是Dessert接口有三个实现类,并且都使用了@Component注解,那么组件扫描的时候,会为这三个实现类实例化bean在Spring上下文中存储,那么setDessert()方法在选择时,由于有三个实现类,不知道使用哪个,Spring则会报错,抛出NoUniqueBeanDefinitionException异常。
解决:我们可以设置某一个为首选bean(使用@Primary)
@Component
@Primary
public class IceCream extends Dessert(){ ......... }
或则通过显示配置的声明IceCream,那么@Bean方法如下所示:
@Bean
@Primiary
public Dessert iceCream(){
return new IceCream();
}
如果你使用的是xml配置的话,那么配置如下:
<bean id = "iceCream" class = "com.desserteater.IceCream" primary="true" />
无论哪种方式,效果一样,都是告诉Spring在遇到歧义性的时候首先该bean.
但是,如果两个bean或则更多的首选bean使用@Primiart,Spring就无法正常工作,因为仍然会有歧义性。
@Qualifier
@Qualifier注解是使用限定符的主要方式,他可以与@Autowired和@Inject协同使用,在注入的时候指定想要注入的bean
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
这是最简单的限定符使用例子,为@Qualifier注解所设置的参数就是想要注入的bean的ID,所有使用@Component注解声明的类都会创建为bean,并且bean的ID为首字母变小写的类名。因此,@Qualifier("iceCream")指向的是组件扫描时所创建的bean,并且这个bean是IceCream类的实例。
更准确的讲,@Qualifier("iceCream")所引用的bean要具有String类型的“iceCream”作为限定符。如果没有指定其他限定符的话,所有的bean都会给定一个默认的限定符,这个限定符与bean的ID相同,即为首字母变小写的类名。因此框架会将有“iceCream”限定符的bean注入到setDessert()方法中。
问题:基于默认的beanID它是为首字母变小写的类名,如果重构了该类,重命名为其他类名的话,比如将IceCream重命名为Gelato,那么上面的setDesser()方法将无法通过限定符匹配,自动装配会失败。
解决:创建自定义限定符⬇️⬇️⬇️
创建自定义的限定符
在bean声明上添加@Qualifier注解,他可以和@Component组合使用
@Component
@Qualifier("cold")
public class IceCream implements Dessert { ...... }
在这种情况下,cold限定符分配给了IceCream bean,因为它没有耦合的类名,所以可以任意重构IceCream的类名,而不必担心上面的破坏自动装配问题。在注入的地方引入cold限定符就可以了。
当通过java显示配置bean的时候,@Qualifier也可以和@Bean一起使用
@Bean
@Qualifier("cold")
public Dessert iceCream(){
return new IceCream();
}
问题:如果相同类型的类都使用一样的@Qualifier注解,那怎么办
Java不允许同一个条目上重复出现相同类型的多个注解。否则会报错,
(注:java 8 允许出现重复的注解,只要这个注解本身定义的时候带有@Repeatable注解就可以。不过Spring的@Qualifier注解并没有在定义时添加@Repeatable注解。)
//java8中允许使用重复注解的demo,注解需要添加@Repeatable
@Repeatable(Authorities.class)
public @interface Authority {
String role();
}
public @interface Authorities {
Authority[] value();
}
public class RepeatAnnotationUseNewVersion {
@Authority(role="Admin")
@Authority(role="Manager")
public void doSomeThing(){ }
}
如:
@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert { ...... }
上面不允许这样操作的,因为@Qualifier没有@Repeatable注解,那么怎么解决呢?
我们可以创建自定义的限定符注解,借助这样的注解来使bean唯一。我们将不再使用@Qualifier("code"),而是使用自定义的@Code注解,如下所示:
@Target( {ElementType.CONSTRUCTOR, ElementType.FIELD ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }
当不想使用@Qualifier时,我们通过自定义限定符注解时,添加@Qualifier注解,自定义注解就觉有@Qualifier注解的特性。他们本身实际上也就成为了限定符注解。
那么如果出现上面那个问题,我们可以按层次定义多个注解即可:如
@Component
@Cold
@Creamy
public class IceCream implements Dessert { ...... }
@Component
@Cold
@Fruity
public class IceCream implements Dessert { ...... }