处理自动装配的歧义性

自动装配可以对依赖注入提供很大帮助,因为它会减少装配应用程序组件时所需的显式装配的数量。

不过,仅有一个bean匹配所需的结果时,自动装配才是有效的。如果不仅有一个bean能够匹配的话,这种歧义性会阻碍Spring自动装配属性,构造器参数或方法参数。

应用场景

场景:我们声明一个接口,有三个类对该接口进行实现,并注入到bean

public interface IAnimal {
    void say();
}

接口实现:

@Component
public class Cat implements IAnimal{

    @Override
    public void say() {
        System.out.println("miao miao ~~~");
    }
}
@Component
public class Dog implements IAnimal {
    @Override
    public void say() {
        System.out.println("wang wang ~~~");
    }
}
@Component
public class Pig implements IAnimal {
    @Override
    public void say() {
        System.out.println("heng heng ~~~");
    }
}

声明一个动物服务类,自动装配注入IAnimal:

@Component
public class AnimalService {

    @Autowired
    private IAnimal animal;

    public void say(){
        animal.say();
    }
}

运行测试:

@ComponentScan
@Configuration
public class MyMain {
    public static void main(String[] args) {
        ApplicationContext context = new  AnnotationConfigApplicationContext(MyMain.class);
        AnimalService animalService = context.getBean(AnimalService.class);
        animalService.say();
    }
}

由于三个实现都使用了@Component注解,在组件扫描的时候,能够发现他们并将其创建为Spring应用上下文中的bean。然后spring试图自动装配AnimalService的IAnimal属性,但是它并没有唯一、无歧义的可选值:在这里插入图片描述
查看异常,大概意思就是说,我们需要一个bean,类型是com.haan.test01.IAnimal,但是在容器中找到了3个cat,dog,pig ,造成了歧义。

当确实发生歧义性时,Spring提供了多种可选方案来解决这样的问题:

  • 将可选bean中的某一个设为首选(primary)bean
  • 使用限定符(qualifier)来帮助Spring将可选的bean的范围缩小到只有一个bean

标志首选的bean

在声明bean的时候,通过将其中一个可选的bean设置为首选(primary)bean能够避免在自动装配时的歧义性。当遇到歧义的时候,Spring会使用首选的bean。
可以通过@Primary来表示设置为首选bean。@Primary可以与@Component组合,也可以与@Bean进行组合。比如,将Cat 实现声明为首选bean:

@Component
@Primary //标识为首选Bean
public class Cat implements IAnimal{

    @Override
    public void say() {
        System.out.println("miao miao ~~~");
    }
}

运行测试:

miao miao ~~~

然而,如果有两个继承相同接口的类同时设置primary,则仍然会有歧义,因此引入Qualifier

限定符指明注入的bean

设置首选的bean的局限性在于@Primary无法将可选方案的范围限定到唯一一个无歧义的选项中。它只能标识一个优先的可选方案,当首选的bean的数量超过一个的时候,将依旧存在歧义。
与之相反,Spring的限定符能够在所有可选的bean上进行缩小范围的操作,最终能够达到只有一个bean满足所规定的限制条件。

@Qualifier 注解是使用限定符的主要方式。他可以与@Autowired和@Inject 协同使用,在注入的时候指明注入bean的name,指明哪个bean将被注入进去。

@Qualifier 注解元源码:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
    String value() default "";	//指定bean名称标识
}

示例:

@Component
public class AnimalService {

    @Autowired
    @Qualifier(value = "dog")
    private IAnimal animal;

    public void say(){
        animal.say();//wang wang ~~~
    }
}
posted @ 2022-10-30 23:22  寒小韩  阅读(26)  评论(0)    收藏  举报