实战Spring高级装配中处理自动装配的歧义性

本文详细探讨了Spring自动装配时出现歧义的原因,即多个bean匹配同一需求。解决方案包括设置首选bean(@Primary)和使用限定符(@Qualifier)。介绍了如何创建自定义限定符注解以解决相同限定符导致的歧义,并提供了使用自定义注解的示例,增强了代码的类型安全性和可读性。
摘要由CSDN通过智能技术生成

目录

自动装配的歧义性怎么出现的?

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

首选 bean

限定自动装配的bean

如何创建自定义的限定符

使用自定义的限定符注解

问题描述

总结


自动装配的歧义性怎么出现的?

产生自动装配的歧义性的原因是,出现了多个 bean 匹配所需的结果。

比如:

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

将Dessert 注入到 setDessert 方法中。但是 Dessert 为一个接口,接口被三个类实现了,分别为:

@Component
public class Cake implements Dessert { }

@Component
public class Cookies implements Dessert { }

@Component
public class IceCream implements Dessert { }

因为这三个类 都带有@Component 注解,也就是在组件扫描的时候,能够发现他们并将其创建为 Spring 应用上下文里面的 bean。然后,当Spring 试图自动装配setDessert()中的Dessert参数时,它并没有唯一、无歧义的可选值。此时会抛出NoUniqueBeanDefinitionException>

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

解决歧义性有以下几种方法:

  1. 在可选 bean 中 某一个设为首选项(primary)
  2. 使用限定符(qualify)将可选的 bean 范围缩小到只有一个 bean

首选 bean

在声明bean的时候,通过将其中一个可选的bean设置为首选(primary)bean能够避免自动装配时的歧义性。

/**
 * ①、@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,效果都是一样的,都是告诉Spring在遇到歧义性的时候要选择首选的bean。

注意:首选bean是唯一的,首选首选肯定只有一个

限定自动装配的bean

之所以有限定自动装配 bean,是因为 设置首选 bean 时,无法 通过@Primary 注解来实现 范围限定到唯一一个无歧义性的选项。你只能选择 其中一个 bean,然而选择不了某一个无歧义性。

@Qualifier注解是使用限定符的主要方式。它可以与@Autowired和@Inject协同使用,在注入的时候指定想要注入进去的是哪个bean。 例如,我们想要确保要将IceCream注入到setDessert()之中:

    /**
     * 想要确保要将 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;
    }

为@Qualifier注解所设置的参数就是想要注入的bean的ID。setDessert()方法上所指定的限定符与要注入的bean的名称是紧耦合的。对类名称的任意改动都会导致限定符失效。

如何创建自定义的限定符

很简单只要在@Component注解的 bean 上面,同样增加  @Qualifier 注解即可,只要这两个注解里面所赋值 一一对应既可,如:

/**
 * cold 限定符分配给了 IceCream bean
 * 此时,没有耦合类名,可以随意重构 IceCream 的类名,而不用担心会破坏自动装配。
 */
@Component
@Qualifier("cold")
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();
    }

使用自定义的限定符注解

问题描述

自定义的限定符要比基于 bean id 的限定符好一定,毕竟可以自定义。但是 即使自定义了,也会出现 多个 bean 使用了相同的自定义限定符。

如果新建一个 bean,再次使用,上一段的    @Qualifier("cold") 这样 我们再次遇到了 歧义性的问题。

解决方法 1(错误方法):在注入点和 bean 定义的地方在新加一个 @Qualifier(" xxx")注解

此时的 冰激凌:

@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert {
}

冷加奶油也是可以做到的,同样在注入点,我们可能会使用这样的方式来将范围缩小到IceCream:

@Autowired
// 注入的地方,引用 cold 限定符即可。在家一个 creamy
@Qualifier("cold")
@Qualifier("creamy")
public void setDessert(Dessert dessert){
   this.dessert = dessert;
}

理论上觉着可以,但是 java 不认同:Java不允许在同一个条目上重复出现相同类型的多个注解。编译器会提示错误!!!

解决方法 2(重构接口Qualifier):既然不能使用多个 注解,那只能自己写注解

看下 Qualifier 接口源码:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

	String value() default "";

}

然后模仿写个 interface:

/**
 * 因为 Java 不允许在同一条目上重复出现相同类型的多个注解
 * 所以使用自定义的 @Cold 来替换 @Qualifier("cold")
 * 它们具有了 @Qualifier 注解的特性。本身实际上就成为了限定符注解。
 */
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}

通过在定义时添加@Qualifier注解,它们就具有 了@Qualifier注解的特性。它们本身实际上就成为了限定符注解。

    // 使用必要的限定符注解进行任意组合,从而将可选范围缩小到只有一个 bean 满足需求。
    @Autowired
    @Cold
    public void setDessert(Dessert dessert){
        this.dessert = dessert;
    }

通过声明自定义的限定符注解,我们可以同时使用多个限定符,不会再有Java编译器的限制或错误。

与此同时,相对于使用原始 的@Qualifier并借助String类型来指定限定符,自定义的注解也更为类型安全。

总结

在本节和前面的节中,我们讨论了几种通过自定义注解扩展Spring的方式。

为了创建自定义的条件化注解,我们创建一个新的注解并在这个注解上添加了@Conditional。

为了创建自定义的限定符注解,我们创建一个新的注解并在这个注解上添加了@Qualifier。

这种技术可以用到很多的Spring注解中,从而能够将它们组合在一起形成特定目标的自定义注解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值