当候选 Bean 数目不为 1 时的应对方法
在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验:
清单 10. 候选 Bean 数目为 0 时
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://www.springframework.org/schema/beans
5. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
6.
7. <bean class="org.springframework.beans.factory.annotation.
8. />
9.
10. <bean id="boss" class="com.baobaotao.Boss"/>
11.
12. <!-- 将 office Bean 注释掉 -->
13. <bean id="office" class="com.baobaotao.Office">
14. <property name="officeNo" value="001"/>
15. </bean>-->
16.
17. <bean id="car" class="com.baobaotao.Car" scope="singleton">
18. <property name="brand" value=" 红旗 CA72"/>
19. <property name="price" value="2000"/>
20. </bean>
21. </beans>
由于 office Bean 被注释掉了,所以 Spring 容器中将没有类型为 Office 的 Bean 了,而 Boss 的 office 属性标注了 @Autowired,当启动 Spring 容器时,异常就产生了。
当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。来看一下具体的例子:
清单 11. 使用 @Autowired(required = false)
1. package com.baobaotao;
2.
3. import org.springframework.beans.factory.annotation.Autowired;
4. import org.springframework.beans.factory.annotation.Required;
5.
6. public class Boss {
7.
8. private Car car;
9. private Office office;
10.
11. @Autowired
12. public void setCar(Car car) {
13. this.car = car;
14. }
15. @Autowired(required = false)
16. public void setOffice(Office office) {
17. this.office = office;
18. }
19. …
20. }
当然,一般情况下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到(如为了快速启动 Spring 容器,仅引入一些模块的 Spring 配置文件),所以 @Autowired(required = false) 会很少用到。
和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子:
清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean
1. …
2. <bean id="office" class="com.baobaotao.Office">
3. <property name="officeNo" value="001"/>
4. </bean>
5. <bean id="office2" class="com.baobaotao.Office">
6. <property name="officeNo" value="001"/>
7. </bean>
8. …
我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean,当对 Boss 的 office 成员变量进行自动注入时,Spring 容器将无法确定到底要用哪一个 Bean,因此异常发生了。
Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了,可以通过下面的方法解决异常:
清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称
1. @Autowired
2. public void setOffice(@Qualifier("office")Office office) {
3. this.office = office;
4. }
5.
@Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码:
对成员变量进行注释:
清单 14. 对成员变量使用 @Qualifier 注释
1. public class Boss {
2. @Autowired
3. private Car car;
4.
5. @Autowired
6. @Qualifier("office")
7. private Office office;
8. …
9. }
对构造函数入参进行注释:
清单 15. 对构造函数变量使用 @Qualifier 注释
1.
2. public class Boss {
3. private Car car;
4. private Office office;
5.
6. @Autowired
7. public Boss(Car car , @Qualifier("office")Office office){
8. this.car = car;
9. this.office = office ;
10. }
11. }
12.
13.
@Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。