@qualifier注解_Spring Annotations每日一解系列之@Qualifier

在上一篇 Spring Annotations #@Autowired中,介绍了使用@Autowired完成自动注入的使用方式,在默认情况下,Spring Framework是根据类型来解析@Autowired所注释的类,如果在Spring的容器中存在多个同种类型的Bean时,Spring Framework将无法完成依赖注入工作。原因是Spring Framework无法完成二选一或者N选一的工作,在此情况下,需要借助@Qualifier注解来指定依赖注入的范围。

542093fce5064565a4c6d91bb7ca6892

Spring Annotations每日一解系列文章

序言

Qualifier[ˈkwäləˌfīər]本身有预选,筛选的意思,其与@Autowired注解一起使用,当需要执行依赖注入时,@Qualifier会限定Spring Framework需要选择注入的对象。这是一个很有用的注解,例如,当项目中一个Service接口有多个实现类时,就可以使用@Qualifier对依赖注入过程进行更详细的控制。

1.@Qualifier与@Autowired

当你的应用程序中有多个同种类型的Bean存在时,永远不要让Spring Framework自己决策在依赖注入的过程中选择哪一个Bean,如果你这样做了,将会收到来自Spring Framework的友情提示(臣妾做不到):

Cause by : org.springframework.beans.factory.NoUniqueBeanDefinitionException

假定这样一个场景,定义一个Animal接口并包含一个speak()方法,然后定义三个类Dog,Cat和Cow实现Animal中定义的speak()方法。首先,只使用@Autowired注解并从Spring容器中获取Animal看会发生什么情况。代码如下所示:

1.1 Just Autowired Annotation

首先,需要开启Spring Framework组件扫描功能:

@Configuration@ComponentScan(basePackages = {"com.ramostear.qualifier.annotation.beans"})public class AppConfig {}

接着,开始创建Animal接口和其他动物类:

public interface Animal {    void speak();}
@Component("cat")public class Cat implements Animal {    @Override    public void speak() {        System.out.println("Meow ~ Meow ~");    }}
@Component("cow")public class Cow implements Animal {    @Override    public void speak() {        System.out.println("Moo~Moo~");    }}
@Component("dog")public class Dog implements Animal{    @Override    public void speak() {        System.out.println("Wang~Wang~");    }}

接下来,创建一个动物园类Zoo,将这些动物都放到动物园里进行管理:

@Componentpublic class Zoo {​    @Autowired    private Animal animal;​    public void animalSpeaking(){        animal.speak();    }}

最后,在main()方法中编写代码测试并观察控制台输出:

@SpringBootApplicationpublic class QualifierAnnotationApplication {​    public static void main(String[] args) {        SpringApplication.run(QualifierAnnotationApplication.class, args);​        AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(AppConfig.class);        Zoo zoo = context.getBean(Zoo.class);        zoo.animalSpeaking();​        context.close();    }​}

运行上述的main()方法,控制台将输出如下信息:

***************************APPLICATION FAILED TO START***************************​Description:​Field animal in com.ramostear.qualifier.annotation.beans.Zoo required a single bean, but 3 were found:    - cat: defined in...    - cow: defined in...    - dog: defined in...Action:    ....or using @Qualifier to identify the bean that should be consumed

补充

这就是文章一开始提到的来自Spring Framewok的善意提示(臣妾做不到!)

如果你使用IntelliJ IDEA这类的代码编辑器,在创建Zoo类时就会提示有多个类型相同的Bean存在,Spring无法自动完成依赖注入,并推荐你使用Qualifier注解指定需要进行注入的对象。

43f44017a0f44cfab1d7c277062daf03

IDEA 提示

1.2 With Qualifier Annotation

接着上一小节的案例,分别创建DogZoo,CatZoo和CowZoo三个类,并使用@Qualifier指定需要注入的对象实例:

@Componentpublic class CatZoo {​    @Autowired    @Qualifier(value = "cat")    private Animal animal;​    public void animalSpeaking(){        animal.speak();    }}
@Componentpublic class CowZoo {    @Autowired    @Qualifier(value = "cow")    private Animal animal;​    public void animalSpeaking(){        animal.speak();    }}
@Componentpublic class DogZoo {​    @Autowired    @Qualifier(value = "dog")    private Animal animal;​    public void animalSpeaking(){        animal.speak();    }}

这样,相当于将原有的动物园进行了区域划分,不同的区域管理不同的动物,接下来,在main()方法中编写代码测试,并在控制台中观察输出结果:

@SpringBootApplicationpublic class QualifierAnnotationApplication {​    public static void main(String[] args) {        SpringApplication.run(QualifierAnnotationApplication.class, args);​        AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(AppConfig.class);​        DogZoo dogZoo = context.getBean(DogZoo.class);        dogZoo.animalSpeaking();​        CatZoo catZoo = context.getBean(CatZoo.class);        catZoo.animalSpeaking();​        CowZoo cowZoo = context.getBean(CowZoo.class);        cowZoo.animalSpeaking();​        context.close();    }​}
Wang ~ Wang ~Meow ~ Meow ~Moo ~ Moo ~​Process finished with exit code 0

2. 自定义Qualifier注解

除了使用Spring Framework提供的@Qualifier注解之外,还可以在@Qualifier的基础上自定义qualifier注解类。在自定义qualifier注解之前,先了解@Qualifier注解类的相关细节:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Qualifier {    String value() default "";}

Spring Framework提供的Qualifier类很简单,只有一个用于表示Spring容器中bean名称的默认值value,因此,自定义qualifier类只需要使用@Qualifier注解进行注释即可。接下来,定义一个用于表示动物类型的qualifier注解AnimalType:

@Qualifier@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface AnimalType {    String value() default "";}

为了与上一节区分开,再定义两个Animal接口的实现类Monkey和Pig,并使用自定义的AnimalType注解进行注释:

@Component@AnimalType(value = "monkey")public class Monkey implements Animal{    @Override    public void speak() {        System.out.println("dai~ dai~");    }}
@Component@AnimalType(value = "pig")public class Pig implements Animal{    @Override    public void speak() {        System.out.println("heng ~ heng ~");    }}

接下来,定义两个类MonkeyZoo和PigZoo,并使用@AnimalType注解限定各自所依赖的对象:

@Componentpublic class MonkeyZoo {​    @Autowired    @AnimalType(value = "monkey")    private Animal animal;        public void monkeyShowTime(){        animal.speak();    }​}
@Componentpublic class PigZoo {​    @Autowired    @AnimalType(value = "pig")    private Animal animal;        public void pigShowTime(){        animal.speak();    }}

同样,在main()方法中编写代码测试上述两个类是否能够分别将Animal注入进来,并观察控制台输出:

@SpringBootApplicationpublic class QualifierAnnotationApplication {​    public static void main(String[] args) {        SpringApplication.run(QualifierAnnotationApplication.class, args);​        AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(AppConfig.class);​        MonkeyZoo monkeyZoo = context.getBean(MonkeyZoo.class);        monkeyZoo.monkeyShowTime();​        PigZoo pigZoo = context.getBean(PigZoo.class);        pigZoo.pigShowTime();                context.close();    }}
dai~ dai~heng ~ heng ~

在此小节中,演示了如何自定义并使用qualifier注解,除了将自定义qualifier注解作用于属性上,还可以将其作用于方法,对象类型以及方法参数上。

3. 根据Bean Name完成自动注入

除上述的方法之外,Spring Framework也提供了基于Bean Name完成依赖注入工作。使用此方式的前提是在类中所依赖的Bean name需要和Spring容器中所管理的Bean name保持一致。如果在使用@Component注解时指定了Bean的名称,则在引用的属性名称必须于@Component注解指定的一致。

@Componentpublic class OtherZoo {​    @Autowired    private Animal monkey;​    public void showTime(){        monkey.speak();    }​}
@SpringBootApplicationpublic class QualifierAnnotationApplication {​    public static void main(String[] args) {        SpringApplication.run(QualifierAnnotationApplication.class, args);​        AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(AppConfig.class);​        OtherZoo otherZoo = context.getBean(OtherZoo.class);        otherZoo.showTime();​        context.close();    }
dai~ dai~

综上所述,当应用程序中出现多个同种类型的Bean时,为了让Spring能够顺利完成依赖注入,使用@Qualifier注解是一个不错的选择,使用@Qualifier对依赖注入过程进行更详细的控制,防止出现二选一,多选一的情况发生。此外,文中还列举了自定义qualifier注解的定义和使用方法,以及基于Bean name完成依赖注入的演示案例。

本文所涉及的源代码已上传到Github,需要的小伙伴可点击下面的链接移步到Github进行下载:

8dc4f14e7a8749d89a33a15c32193fb3

https://github.com/ramostear/spring-annotations-daily-example

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值