3.3 处理自动装配的歧义性

在第 2 章中,我们已经看到如何使用自动装配让 Spring 完全负责将 bean 引用注入到构造参数和属性中。自动装配能够提供很大的帮助,因为它会减少装配应用程序组件时所需要的显式配置的数量。

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

为了阐述自动装配的歧义性,假设我们使用 @Autowired 注解标注了 setDessert() 方法:

@Autowired
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}

在本例中,Dessert 是一个接口,并且有三个类实现了这个接口,分别为 Cake、Cookies 和 IceCream:

@Component
public class Cake implements Dessert { ... }
@Component
public class Cookies implements Dessert { ... }
@Component
public class IceCream implements Dessert { ... }

因为这三个实现均使用了 @Component 注解,在组件扫描的时候,能够发现它们并将其创建为 Spring 应用上下文里面的 bean。然后,当 Spring 试图自动装配 setDessert() 中的 Dessert 参数时,它并没有唯一、无歧义的可选值。在从多种甜点中做出选择时,尽管大多数人并不会有什么困难,但是 Spring 却无法做出选择。Spring 此时别无他法,只好宣告失败并抛出异常。更精确地讲,Spring 会抛出 NoUniqueBeanDefinitionException:

nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.desserteater.Dessert] is defined:
expected single matching bean but found 3: cake,cookies,iceCream

当然,使用吃甜点的样例来阐述自动装配在遇到歧义性时所面临的问题多少有些牵强。在实际中,自动装配歧义性的问题其实比你想象中的更为罕见。就算这种歧义性确实是个问题,但更常见的情况是给定的类型只有一个实现类,因此自动装配能够很好地运行。

但是,当确实发生歧义性的时候,Spring 提供了多种可选方案来解决这样的问题。你可以将可选 bean 中的某一个设为首选(primary)的 bean,或者使用限定符(qualifier)来帮助 Spring 将可选的 bean 的范 缩小到只有一个 bean。