• 解决自动装配的歧义


    @Autowired注解能让Spring容器找到类型匹配的Bean之后自动进行装配。同时,这也引出这样一个问题:“假如Spring容器存在多个类型相同的Bean,Spring容器怎么知道应该自动装配哪个Bean呢?”举个例子,假如com.dream包现有这样一些类:

    1 public interface Music {
    2 }
    1 @Component
    2 public class PopMusic implements Music {
    3 }
    1 @Component
    2 public class ClassicMusic implements Music {
    3 }
    1 @Component
    2 public class CountryMusic implements Music {
    3 }
    1 @Component
    2 public class Player {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public Player(Music music) {
    7         this.music = music;
    8     }
    9 }

    这些代码定义了一个Music接口;三个实现了Music接口的PopMusic类,ClassicMusic类,CountryMusic类;一个Player类。其中,PopMusic类,ClassicMusic类,CountryMusic类,Player类是组件,能被组件扫描发现之后进行创建。另外,Player构造函数带有@Autowired注解,能够自动装配Music类型的Bean。可是,当我们觉得万事具备,开始运行时却发现程序抛出NoUniqueBeanDefinitionException类型的异常,根本无法完成Bean的创建与装配。

    这是怎么回事呢?

    原来,Spring容器创建Player时,发现Spring容器存在PopMusic,ClassicMusic,CountryMusic三个Music类型的Bean,一时之间不知如何选择,于是抛出NoUniqueBeanDefinitionException类型的异常。

    那该怎么办呢?

    一种解决方法是引入@Primary注解,把某个Bean标为首选。这样,当类型相同的Bean多于一个时,Spring容器就知道应该选用首选的那个进行装配了。因此,如果我们想让Spring容器选用PopMusic,可把PopMusic标为首选,如下所示:

    1 @Primary
    2 @Component
    3 public class PopMusic implements Music {
    4 }

    现在,PopMusic带有@Primary注解。这样,Spring容器装配Player时,就知道该在PopMusic,ClassicMusic,CountryMusic三个Music类型的Bean里选用PopMusic了。

    还有一种情况。假如现有两种音乐播放器:一种是DVD播放器;一种是数字播放器。定义如下:

    1 @Component
    2 public class DvdPlayer {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public DvdPlayer(Music music) {
    7         this.music = music;
    8     }
    9 }
    1 @Component
    2 public class DigitalPlayer {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public DigitalPlayer(Music music) {
    7         this.music = music;
    8     }
    9 }

    可以看到DvdPlayer,DigitalPlayer的构造函数参数都是Music类型的。如果我们希望DvdPlayer播放的是PopMusic,DigitalPlayer播放的是ClassicMusic。这时,该怎么办呢?

    毫无疑问,@Primary注解只能标注一个首选,而且Spring容器只会选用那个标为首选的Bean进行装配。这意味着光靠@Primary注解根本无法达成我们的目的。

    那该怎么办呢?

    直觉告诉我们,Spring还提供了其它方式用于解决自动装配存在的歧义问题。事实也确实如此。@Qualifier注解就是Spring提供的另一种方式。因此,我们可以使用@Qualifier注解这样解决问题:

    1 @Component
    2 @Qualifier("popMusicQualifier")
    3 public class PopMusic implements Music {
    4 }
    1 @Component
    2 public class DvdPlayer {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public DvdPlayer(@Qualifier("popMusicQualifier") Music music) {
    7         this.music = music;
    8     }
    9 }
    1 @Component
    2 @Qualifier("classicMusicQualifier")
    3 public class ClassicMusic implements Music {
    4 }
    @Component
    public class DigitalPlayer {
        private Music music = null;
    
        @Autowired
        public DigitalPlayer(@Qualifier("classicMusicQualifier") Music music) {
            this.music = music;
        }
    }

    可以看到@Qualifier注解能以限定符的方式指定需要注入的Bean,具体如下:
    1.添加@Qualifier注解到Bean上,指定Bean的限定符为某值
    2.添加@Qualifier注解到需要注入依赖的参数旁边,指定即将注入的Bean须是限定符为某值的Bean

    这样之后,Bean与Bean的注入就通过限定符关联起来了。自然而然的,Spring容器也就知道怎样通过限定符找到匹配的Bean进行自动装配了。

    于是,我们在PopMusic类上添加了@Qualifier("popMusicQualifier")注解,在DvdPlayer的构造函数的参数旁边也添加了@Qualifier("popMusicQualifier")注解,从而告诉Spring容器把PopMusic注入DvdPlayer的构造函数中;我们在ClassicMusic类上添加了@Qualifier("classicMusicQualifier")注解,在DigitalPlayer的构造函数参数旁边也添加了@Qualifier("classicMusicQualifier")注解,从而告诉Spring容器把ClassicMusic注入DigitalPlayer的构造函数中。

    还有,Bean的限定符默认是Bean的ID。我们知道添加@Component注解之后,Spring容器创建的Bean其ID默认是类名的第一个字母变成小写之后的字符串。也就是说,PopMusic这个Bean的ID是popMusic,ClassicMusic这个Bean的ID是classicMusic。因此,我们还能把@Qualifier注解从PopMusic类和ClassicMusic类上删掉,而后指定DvdPlayer,DigitalPlayer的构造函数参数旁边的@Qualifier注解的限定符为Bean的ID。如下所示:

    1 @Component
    2 public class DvdPlayer {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public DvdPlayer(@Qualifier("popMusic") Music music) {
    7         this.music = music;
    8     }
    9 }
    1 @Component
    2 public class DigitalPlayer {
    3     private Music music = null;
    4 
    5     @Autowired
    6     public DigitalPlayer(@Qualifier("classicMusic") Music music) {
    7         this.music = music;
    8     }
    9 }

    于是,关于自动装配的歧义问题,我们已经知道怎么解决了。下章该谈谈属性占位符,看看怎样把属性文件里的值读取出来之后通过@Value注解注入Bean里。欢迎大家继续阅读,谢谢大家!

    返回目录    下载代码

  • 相关阅读:
    JAVA调用WebService总结
    关于购物车的想法
    ASP.NET中初试Ajax
    转帖:从FxCop归纳出来的一些规范建议
    数据结构(二叉树)C#描述
    FormView控件和DetailsGridView控件实现MasterSlave
    在.NET中使用MySql数据库
    Oracle学习总结1
    Oracle学习总结2
    关于字符匹配所引起的的问题
  • 原文地址:https://www.cnblogs.com/evanlin/p/15860789.html
Copyright © 2020-2023  润新知