spring 之 注入之 by name or by type, or both ?

@Autowired 和  @Qualifier 

使用xml 注入的时候, 我们可以指定 autowire=“byType” 或“byName” 。

但是使用 注解的时候, @Autowired  只有一个 required 属性, 无法设置  by name或者 by type。 那么 这个时候, 我们可以使用 @Qualifier 

 @Autowired  @Qualifier 需要一起使用,他们是一个奇怪的组合, 组合到一起的时候, 表示,先尝试 by type, 出现冲突了,那么by name。 换句话说, 如果需要注入的某个类型的bean ,只有一个实例, 对应  @Autowired  @Qualifier  组合, 其中@Qualifier 可有可无, 有的话, 其 value 可以随便写。但是 如果有多个, 那就不一样的: 此时,@Qualifier也必须正确。 

关于 @Bean

spring 在解析到 @Bean 的时候, 会自动给 参数中的 对象类型 注入 一个实例, 默认byType , 不需要 @Autowired 或者 @Qualifier 

    @Bean
    public LkCar datasource(LkBean lkBean) {
        System.out.println("config datasrouce.");
        return new LkCar(lkBean);
    }

    @Bean
    public LkBean aa() {
        System.out.println("config bean aa");
        LkBean bean = new LkBean();
        bean.setStr("aa");
        return bean;
    }

    @Bean("bb")
    // @Primary
    public LkBean aabb() {
        System.out.println("config bean bb");
        LkBean bean = new LkBean();
        bean.setStr("bb");
        return bean;
    }

java config 默认是使用 byType注册。 上面的代码中, 有两个类型为 LkBean的实例, 故实例化 LkCar的时候出错:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'datasource' defined in com.anno.LkAnno: Unsatisfied dependency expressed through method 'datasource' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.anno.LkBean' available: expected single matching bean but found 2: aa,bb
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:467)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1134)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1028)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
    at AnnoIo22CTest.main(AnnoIo22CTest.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.anno.LkBean' available: expected single matching bean but found 2: aa,bb
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:172)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1114)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 19 more

解决方法有两个, 

1 在其中一个参数bean 的方法上面添加 @Primary , 表明它是首选

2 把 datasource 签名改为: 

public LkCar datasource(@Qualifier("bb")LkBean lkBean) {
...

这里的 @Autowired  是不需要的。 @Qualifier 必须放到 方法的参数中去, 而不是在 方法之上。

另外, 当一个类中有两个@Bean , 比如一个 为方法aa, 另一个返回值相同,但是@Bean("aa"), 那么, 前面的生效, 后面的无法注册为 spring 的bean, 因为已经注册过了一次了!

另外, 当我们有多个配置类的时候,  它们是会合并的,合并的规则是:

 如果没有同名bean ,当然就简单的组合到一起就行了。

如果有冲突呢?  后面出现的覆盖前面的!!  这和 在一个 java config 中出现冲突的处理 顺序是相反的!!

另外, 我发现, 当我 @Autowire 一个 方法的时候, 如果在当前类有多个可选同类型 bean 实例,那么 会出错, 但是如果其他的 java config 配置类 还有一个 可选的同类型bean 实例, 那么就不会出错。 

真是奇了怪了。  究其原因, 应该是 跟 bean 实例化的顺序有关, 如果 在Autowire 一个 方法的时候, 能够找到一个实例, 而不是多个, 那么久使用它。 否则如果发现多个, 那么久不行。!! 

注意: @Bean 或者  java config 仅仅是定义及 注册了 bean 的定义,  真正的实例化是 在第一次使用它的时候(如果有冲突, 是这个时候才会发现)。

原文地址:https://www.cnblogs.com/FlyAway2013/p/7857406.html