10-spring @Primary和@Qualifier辨析

@Primary和@Qualifier

一、代码

  • 下面代码中,我声明了2个TestService类型的bean,一个是@Component方式,一个是@Bean的方式,我们通过其内部的成员变量不一样来区分到底打印的是哪一个bean实例。既然有2个bean,那么问题就来了,我们知道bean有一个beanid,前面的方式是类名首字母小写,后面的方式是方法名,按照2个bean的id的是否相同,问题又分为几种情况,结合后面要引入的@Primaey和@Qualifier注解,我们按照情况的分类一个一个来看。
  • TestController 代码
@Component
public class TestController {

    //@Qualifier("testService2")
    @Autowired
    TestService testService;

    public void printService() {
        testService.printFlag();
    }

}
  • TestService 代码
@Component
public class TestService {

    int flag = 1;

    public void printFlag() {
        System.out.println("flag:" + flag);
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }
}
  • Cap9MainConfig 配置类代码
@Configuration
@ComponentScan("com.intellif.ch09")
public class Cap9MainConfig {

    @Bean
    public TestService testService() {
        TestService testService = new TestService();
        testService.setFlag(2);
        return testService;
    }
}
  • Ch9Test 测试代码
public class Ch9Test {

    @Test
    public void test01() {
        ApplicationContext app = new AnnotationConfigApplicationContext(Cap9MainConfig.class);

        System.out.println("The beans in the IOC container are listed as follows: ");
        //1.打印IOC容器中所有的 实例对象名称
        String[] names = app.getBeanDefinitionNames();
        for ( String name : names ) {
            System.out.println( name );
        }

        System.out.println("Get Controller bean and check the service bean:");
        //2.通过类型获取
        TestController testController = (TestController) app.getBean(TestController.class);
        testController.printService();

        //3.通过类型获取
        System.out.println("Get the service bean by type:");
        TestService testService = (TestService) app.getBean(TestService.class);
        testService.printFlag();

        //4.通过Id获取
        System.out.println("Get the service bean by beanId :");
        TestService testService1 = (TestService) app.getBean("testService");
        testService1.printFlag();

        ((AnnotationConfigApplicationContext) app).close();
    }
}

情况1: bean Id一致

  • bean Id一致,就是我们上面的代码,我们打印的情况如下,我们看到容器中只有一个testService的bean,也就是说在beanid一样的情况下,@Bean将@Component的bean覆盖了。
    到此我们得到结论1。
  • 结论1:在beanid一样的情况下,@Bean注入的bean将@Component注入的bean覆盖了
The beans in the IOC container are listed as follows: 
cap9MainConfig
testController
testService
Get Controller bean and check the service bean:
flag:2
Get the service bean by type:
flag:2
Get the service bean by beanId :
flag:2

情况2: bean Id不一致

  • 这里我们将@Bean的名字修改一下:@Bean(“testService2”),然后测试结果如下,我们看到容器中包含testService和testService2两个bean,Controller中注入的是@Compnent的那个,但是通过类型获取bean报错了,提示TestService类型的bean有 testService,testService2这2个,到这里我们可以得出两个结论:

  • 结论2:@Autowire注入的bean,是通过变量名字去寻找bean的,变量名就是bean id。(这里我们修改TestController中@Autowire TestService的变量名字为testService2,我们会发现打印的就是2)。

  • 结论3:容器中存在多个同类型bean时,通过类型获取会报错,只能通过id获取。(这里我们将测试代码中的通过id获取放在前面就发现,通过id获取是ok的)

The beans in the IOC container are listed as follows: 
cap9MainConfig
testController
testService
testService2
Get Controller bean and check the service bean:
flag:1
Get the service bean by type:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.intellif.ch09.service.TestService' available: expected single matching bean but found 2: testService,testService2

情况3: bean Id不一致,声明@Primary

  • 如果出现了情况2,通过类型获取会失败,那是不是说容器内同类型bean有多个的时候,不能通过类型获取呢?也还是可以的,我们可以指定一个老大,比如同类型存在bean1,bean2,bean3…,我们指定一个老大,通过类型获取的时候就获取这个老大,在其他的地方注入的时候,也会注入这个老大。测试的方法就是在TestService类上加上@Primary,打印如下,我们发现还是有2个bean,但是Controller中注入的,通过类型获取的都是@Primary标注的那个,这就是@Primary注解的作用,到此我们得出结论。

  • 结论4:同类型存在多个bean时,申明了@Primary的bean在被使用时会被优先使用。
    比如app.getBean(TestService.class)通过类型从容器获取bean,或者@Autowire注入(这里注入即使改变变量名指定id也没用了,我们将变量名字改为testService2,发现打印的还是1),仿佛只要@Primary这个bean存在,你就只能看到这个bean,看不到其他的了。

  • 结论5:@Primary指定了bean时,@Autowire的变量名代表注入bean的bean id无效了,会直接注入@Primary标注的bean

The beans in the IOC container are listed as follows: 
cap9MainConfig
testController
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@d70c109: startup date [Sun May 05 15:30:27 CST 2019]; root of context hierarchy
testService
testService2
Get Controller bean and check the service bean:
flag:1
Get the service bean by type:
flag:1
Get the service bean by beanId :
flag:1

情况4: bean Id不一致,声明@Primary和@Qualifier

  • 沿着情况3,感觉有点简单粗暴啊,如果有3个同类型的bean,bean1申明了@Primary,但是我想注入bean2或其他的怎么办?那么就使用@Qualifier,在第三步的基础上,我们在Controller中使用该注解,打印如下,
    我们发现,通过类型获取到的还是bean1,但是Controller中注入的却是bean2,因为我们通过@Qualifier(“testService2”)指定了bean id,到此我们有结论:

  • 结论6:@Qualifier注解可以指定注入bean的beanid,此时只会注入指定beanid的bean,Primary的限制此时不生效了,注意如果指定的beanid有误,那么会报错的。对于没有使用@Qualifier来获取指定bean的地方,依赖注入的还是@Primary标注的bean

@Component
public class TestController {

    @Qualifier("testService2")
    @Autowired
    TestService testService2;

    public void printService() {
        testService2.printFlag();
    }

}
打印:
The beans in the IOC container are listed as follows: 
cap9MainConfig
testController
testService
testService2
Get Controller bean and check the service bean:
flag:2
Get the service bean by type:
flag:1
Get the service bean by beanId :
flag:1

情况5: bean Id不一致,只声明@Qualifier

  • 在情况4中我们的分析可以看出,@Qualifier指定了beanid的情况下,Primary是对我这个类是无效的,不管众多bean中哪一个有@Primary哪一个没有,我只要我自己@Qualifier指定的那个。因此在没有@Primary的情况下也是一样的,只会去找@Qualifier指定的bean,和Primary没关系。

二、总结

  • 1.如果2种方式注入bean且bean id一样时,@Bean注册的方式会覆盖@Component扫描注册的bean。
  • 2.容器中存在多个同类型bean时,但是注入的beanid不一样,那么通过@Autowire注入会去找id和自己相符的那一个,变量名就是bean id,
  • 3.容器中存在多个同类型bean时,通过类型获取会报错,只能通过id获取。
  • 4.容器中存在多个同类型bean时,声明了@Primary的bean在被使用时会被优先使用。比如app.getBean(TestService.class)通过类型从容器获取bean,或者@Autowire注入(这里注入即使改变变量名指定id也没用了),仿佛只要@Primary这个bean存在,你就只能看到这个bean,看不到其他的了。
  • 5.容器中存在多个同类型bean时,@Qualifier注解可以指定注入bean的beanid,此时只会注入指定beanid的bean,@Primary的限制此时不生效了,注意如果指定的beanid有误,那么会报错的。

三、个人理解

  • @Primary意思是,在A类的多个bean里面,我的优先级最高,相当于看不到其他A类型的bean了,@Primary更像是很多bean候选者里面的一个选择准则,当有多个的时候,优先选我。@Qualifier意思是,我依赖注入A类的bean,我只要我指定id的那一个,其他的我不认,@Qualifier则是选择者自己知道自己要哪一个,不管是1个还是多个,我只要我指定的哪一个。前者是提供方提供时候的策略,后者是使用方的选择策略。

示意图

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring中的@Primary和@Qualifier是两个常用的注解,用于解决当有多个同类型的bean需要注入时的歧义问题。 @Primary注解用在bean的定义上,表示该bean是优先选择的bean。当有多个同类型的bean需要注入时,Spring会优先选择被@Primary注解标记的bean作为注入的对象。 @Qualifier注解用在注入点上,可以和@Autowired、@Resource等注解一起使用,用来指定具体要注入的bean的名称。通过在@Qualifier中指定bean的名称,可以明确告诉Spring要选择哪个bean进行注入。 举个例子来说明,假设有一个接口类型的bean,下面有两个实现类A和B都实现了这个接口,并且都被Spring管理。 当我们在其他地方需要注入这个接口类型的bean时,如果不使用@Qualifier和@PrimarySpring会产生歧义,无法确定注入哪个实现类的对象。 使用@Primary注解,我们可以在实现类A上标注@Primary,表示A是优先选择的bean。 当需要注入接口类型bean时,Spring会自动选择A作为注入的对象。 使用@Qualifier注解,我们可以在注入点上标注@Qualifier,然后在@Qualifier中指定具体要注入的bean的名称。 比如,可以使用@Qualifier("beanA")明确指定要注入的bean是A。 以上就是@Primary和@Qualifier的基本用法和含义,通过使用这两个注解可以很方便地解决多个同类型bean的注入问题。 ### 回答2: spring中的@Primary和@Qualifier是依赖注入中的两个注解。 @Primary注解用于标注一个类为首选的bean。当有多个类型相同的bean存在时,spring将会优先选择被@Primary注解标注的bean进行注入。 @Qualifier注解可以与@Autowired一起使用,用于解决存在多个类型相同的bean时的歧义性。通过为@Autowired指定@Qualifier注解的value属性,可以指定具体注入哪个bean。 例如,假设我们有一个接口Person,有两个实现类:Student和Teacher。如果我们在Spring配置文件中同时注册了这两个bean,那么在注入Person类型的依赖时就会有歧义。此时,我们可以使用@Primary注解为其中一个实现类标注为首选bean。这样,在需要注入Person类型的地方,spring就会优先选择被@Primary注解标注的实现类进行注入。 另一种情况是,如果我们不想使用@Primary注解来标注首选bean,而是希望根据特定的条件进行选择注入,就可以使用@Qualifier注解。@Qualifier注解需要与@Autowired注解一起使用,通过指定value属性来指定注入哪个bean。比如,我们可以在@Autowired注解后添加@Qualifier("student")来指定注入的是Student类型的bean。 通过@Primary和@Qualifier注解,我们可以在存在多个类型相同的bean时,精确控制注入的对象,避免歧义性。 ### 回答3: Spring是一个开源的Java框架,提供了依赖注入(Dependency Injection)的功能。在Spring中,我们可以使用@Primary和@Qualifier注解来处理多个相同类型的实例。 @Primary注解用于标识在多个相同类型的Bean中,哪个Bean应该作为首选的Bean。当我们不使用@Qualifier注解指定具体的Bean时,Spring会自动选择使用带有@Primary注解的Bean。例如,当我们有多个实现了同一个接口的类时,通过使用@Primary注解,我们可以指定一个类作为主要实现。 @Qualifier注解用于在多个相同类型的Bean中选择特定的Bean。当我们有多个相同类型的Bean需要注入时,在使用@Autowired或@Inject注入时,我们可以使用@Qualifier注解指定具体的Bean。@Qualifier注解的参数可以是Bean的名字或者是自定义的标识符。 举个例子来说,假设我们有一个接口Animal,有两个实现类Dog和Cat。如果我们希望将Cat作为首选的实现类,我们可以在Cat类上使用@Primary注解。如果有一个需要注入Animal类型的类,但我们想注入Dog,我们可以在注入的地方使用@Qualifier("dog")注解来指定具体的Bean。 总之,@Primary注解用于标识首选的Bean,@Qualifier注解用于指定特定的Bean。它们可以一起使用来处理多个相同类型的实例,使得依赖注入更加灵活和精确。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值