Spring入门(八):自动装配的歧义性 (2)

使用默认的限定符虽然解决了问题,不过可能会引入一些问题。比如我在重构代码时,将IceCream类名修改成了Gelato:

package chapter03.ambiguity; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Component @Primary public class Gelato implements Dessert { @Override public void showName() { System.out.println("冰激凌"); } }

此时运行代码,会发现抛出org.springframework.beans.factory.NoSuchBeanDefinitionException异常,如下所示:

Spring入门(八):自动装配的歧义性

这是因为IceCream重命名为Gelato之后,bean ID由iceCream变成了gelato,但我们注入地方的代码仍然使用的是iceCream这个bean ID,导致没有找到匹配条件的bean。

鉴于使用默认的限定符的这种局限性,我们可以使用自定义的限定符来解决这个问题。

为不影响后面代码的测试结果,将Gelato类再改回IceCream

3.2 基于面向特性的限定符

为了避免因为修改类名而导致自动装配失效的问题,我们可以在@Component或者@Bean注解声明bean时添加上@Qualifier注解,如下所示:

package chapter03.ambiguity; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier("cold") public class IceCream implements Dessert { @Override public void showName() { System.out.println("冰激凌"); } }

然后在注入的地方,不再使用默认生成的bean ID,而是使用刚刚指定的cold限定符:

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

运行测试代码,输入结果如下所示:

冰激凌

此时将IceCream类重命名为Gelato,代码可以正常运行,不会受影响。

然后有一天,某位开发又新建了类Popsicle,该类也使用了cold限定符:

package chapter03.ambiguity; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier("cold") public class Popsicle implements Dessert { @Override public void showName() { System.out.println("棒冰"); } }

此时又带来了新的歧义性问题,因为Spring又不知道该如何选择了,运行代码会抛出org.springframework.beans.factory.NoUniqueBeanDefinitionException异常,如下所示:

Spring入门(八):自动装配的歧义性

此时,我们就需要用到自定义的限定符了。

3.3 自定义的限定符注解

首先,我们新建以下3个注解:

package chapter03.ambiguity; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Cold { } package chapter03.ambiguity; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Creamy { } package chapter03.ambiguity; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Fruity { }

注意事项:这3个注解在定义时都添加了@Qualifier注解,因此它们具有了@Qualifier注解的特性

然后将IceCream类修改为:

package chapter03.ambiguity; import org.springframework.stereotype.Component; @Component @Cold @Creamy public class IceCream implements Dessert { @Override public void showName() { System.out.println("冰激凌"); } }

将Popsicle类修改为:

package chapter03.ambiguity; import org.springframework.stereotype.Component; @Component @Cold @Fruity public class Popsicle implements Dessert { @Override public void showName() { System.out.println("棒冰"); } }

最后,修改下注入地方的代码,使其只能匹配到1个满足条件的bean,如下所示:

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

运行测试代码,输出结果如下所示:

冰激凌

由此,我们也可以发现,自定义注解与@Qualifier注解相比,有以下2个优点:

可以同时使用多个自定义注解,但@Qualifier注解只能使用1个

使用自定义注解比@Qualifier注解更为类型安全

4. 源码及参考

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

5. 最后

打个小广告,欢迎扫码关注微信公众号:「申城异乡人」,定期分享Java技术干货,让我们一起进步。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppjws.html