引言
使用@Autowired自动注入时:
如果注入的接口有多个实现类,如下所示:
那么如果不指定具体是哪个实现类的Bean,在Spring Boot启动时就会发生异常(下图请点击查看):
异常的描述信息非常简单,就是告诉开发者,自动注入的地方需要一个Bean,而spring 容器却找到了3个实现类的Bean。
Action(可以采取的行动): 考虑使用@Primary注解或@Qualifier注解。
@Primary
此注解用在类上面,官方解释如下:
简单翻译过来就是,这个注解相当于Spring 的xml配置文件中bean标签里的 primary属性。
它表示在需要自动注入一个单值依赖的地方,却有多个候选依赖,那么这个注解会指定一个类作为preference(偏好)选择。
可以简单理解为,我们把@Primary注解标记在任意一个类上面,在使用@Autowired注入的时候,如果不特殊指明(如何特殊指明请看@Qualifier的讲解),那么默认就注入被@Primary标记的类。
但是只可以指定一个类作为偏好类,否则依然会产生冲突。
@Qualifier
此注解用在属性上、方法上、参数上等,官方解释如下:
翻译过来就是说,这个注解用在属性上,或参数上,在自动注入的时候作为多个候选bean的限定。也可以使用在其他的随后会被用于限定bean的定制注解上。
其实大多数用于属性上和@Autowired一起联用。
这个注解有一个参数:
(default关键字应该是java8中引入的关键字,不过我也是第一次见过这种写法,可能是孤陋寡闻,各位大神轻喷)
稍微思考一下就可以猜到,如果不为@Qualifier注解指定参数那么默认使用类的默认别名,即类的首字母小写,比如:
ChineseTeacher类的默认别名是:chineseTeacher;
EnglishTeacher类的默认别名是:englishTeacher;
MathTeacher类的默认别名是:mathTeacher。
使用默认别名的方法很简单,直接用类的默认别名来命名我们的自动注入对象,如下所示:
Teacher接口有三个实现类:ChineseTeacher、EnglishTeacher、MathTeacher,我们不需要使用@Primary,也不使用@Qualifier,直接在自动注入的变量上使用默认别名,依然可以注入成功。
是的,当我们在自动注入的时候使用类的默认别名,就不会发生候选Bean冲突的问题。
使用@Qualifier或@Primary注解仅仅是在你不想用默认别名来命名注入的bean或是忘记了使用默认别名的情形。
当我们不使用默认别名来使用自动注入功能,我们就需要使用@Qualifier来指定选择的类型,像下面这样:
注意,如果想将ChineseTeacher注入到teacher对象中,就必须给ChineseTeacher类设置别名。
@Service、@Component等注解都可以传入一个字符串作为类的别名。
注意:使用@Qualifier注入bean的时候,它所选取的类的别名需要与具体的类的别名一致,否则会报无法找到指定类型的错误。
总结
罗里吧嗦说了这么多,其实总结起来很简单。
我们自动注入的时候,如果有多个候选实现类的bean,spring boot启动的时候就不知道该选择哪个bean进行注入,因此会报错。
使用@Primary可以指定一个首选Bean注入;使用@Qualifier可以在自动注入的地方通过传入一个限定名(也就是类的别名)来选取指定的实现类,只不过必须与类的别名一致(如果不传限定名,就使用类的默认别名)
如果既不用@Primary也不用@Qualifier,那就必须在自动注入的时候直接以类的默认别名来命名。
给(spring ioc容器管理的)类设置别名的方式是:@Service("别名")、@Component("别名") 、@Bean("别名") 等等。