高级装配 —— 如何处理自动装配的歧义性?

Q:自动装配时,什么情况下会产生歧义性?歧义性会导致什么?怎么处理?
A:如果不仅有一个 bean 能够匹配结果的话,就会出现歧义性。
它会阻碍 Spring 自动装配属性、构造器参数或方法参数,并抛出 NoUniqueBeanDefinitionException 异常。实际中,歧义性还是比较罕见的。
当出现歧义性的时候,Spring 提供了多种可选方案来解决:你可以将可选的 bean 中的某一个设为首选(primary)的 bean ; 或者使用限定符(qualifier)来帮助 Spring 将可选的 bean 的范围缩小到只有一个 bean

出现歧义性的代码:

    // 自动装配 Dessert 参数时,并没有唯一、无歧义的可选值。
    @Autowired
    public void setDessert(Dessert dessert){
        this.dessert = dessert;
    }
@Component
public class Cake implements Dessert { }

@Component
public class Cookies implements Dessert { }

@Component
public class IceCream implements Dessert { }
Q:如何标示首选的 bean?

A:如下代码所示:

/**
 * ①、@Primary 能够与 @Component 组合用在组件扫描的 bean 上。
 */
@Component
@Primary
public class IceCream implements Dessert { }

/**
 * ②、也可以与 @Bean 组合用在 Java 配置的 bean 声明中。
 */
@Bean
@Primary
public Dessert iceCream(){
    return new iceCream();
}

<!-- ③、XML 配置中,实现同样的功能 -->
<bean id="iceCream" class="com.desserteater.IceCream" primary="true"/>

注意:如果你标示了两个或更多的首选 bean,那么它就无法正常工作了。

就解决歧义性问题而言,限定符更为强大。

设置首选bean的局限性:无法将可选方案的范围限定到唯一一个无歧义性的选项中。他只能标识一个优先的可选方案,当首选 bean 的数量超过一个时,无法进一步缩小可选范围。

Q:如何限定自动配置的 bean?

Spring 的限定符能够在所有可选的 bean 上进行缩小范围的操作,最终能够达到只有一个 bean 满足所规定的限制条件。

A:@Qualifier 注解使用限定符的主要方式。它可以与 @Autowired@Inject 协同使用,在注入的时候指定想要注入进去的是哪个 bean。代码如下:

    /**
     * 想要确保要将 IceCream 注入到 setDessert()之中。
     * 这是使用限定符的最简单的例子。
     * @param dessert
     */
    @Autowired
    // 参数就是想要注入的 bean 的ID。
    // @Qualifier("iceCream") 所引用的 bean 要具有 String 类型的"iceCream"作为限定符,
    // 所有的 bean 都会给定一个默认的限定符,这个限定符与 bean 的 ID 相同。
    @Qualifier("iceCream")
    public void setDessert(Dessert dessert){
        this.dessert = dessert;
    }

此时,方法上所指定的限定符与要注入的 bean 的名称是紧耦合的。

Q:如何创建自定义的限定符?

我们可以为 bean 设置自己的限定符,而不是依赖于将 bean ID 作为限定符。

A:在 bean 声明上添加 @Qualifier 注解。

/**
 * cold 限定符分配给了 IceCream bean
 * 此时,没有耦合类名,可以随意重构 IceCream 的类名,而不用担心会破坏自动装配。
 */
@Component
@Qualifier("cole")
public class IceCream implements Dessert {
}

    @Autowired
    // 注入的地方,引用 code 限定符即可。
    @Qualifier("cole")
    public void setDessert(Dessert dessert){
        this.dessert = dessert;
    }
    // 当通过 Java 配置显示定义 bean 时,也可以与 @Bean 注解一起使用
    @Bean
    @Qualifier("cold")
    public Dessert iceCream(){
        return new IceCream();
    }

为了能够使用多个限定符,我们需要自定义限定符注解。

Q:如何使用自定义的限定符注解?
A:

/**
 * 因为 Java 不允许在同一条目上重复出现相同类型的多个注解
 * 所以使用自定义的 @Cold 来替换 @Qualifier("cold")
 * 它们具有了 @Qualifier 注解的特性。本身实际上就成为了限定符注解。
 */
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
    // 使用必要的限定符注解进行任意组合,从而将可选范围缩小到只有一个 bean 满足需求。
    @Autowired
    @Cold
    @Creamy
    public void setDessert(Dessert dessert){
        this.dessert = dessert;
    }

上一篇:高级装配 —— 条件化的 bean
下一篇:高级装配 —— 如何在不同的作用域中声明 bean?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值