上一篇文章说了,当容器中某种类型的bean存在多个的时候,此时如果我们从容器中查找这种类型的bean的时候,会报下面这个异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException
当从容器中按照类型查找一个bean对象的时候,容器中却找到了多个匹配的bean,此时spring不知道如何选择了,就会报这个异常。
这种异常主要出现在2种情况中:
1,调用getBean查找符合指定类型的bean
<T> T getBean(Class<T> requiredType) throws BeansException;
2,自动注入时方式设置为byType
public interface Service {
}
public class ServiceImpl1 implements Service {
}
public class ServiceImpl2 implements Service {
}
public class SetterBean {
private Service service;
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="impl1" class="com.chen.chat.chen.ServiceImpl1"/>
<bean id="impl2" class="com.chen.chat.chen.ServiceImpl2"/>
<bean id="setterBean" class="com.chen.chat.chen.SetterBean" autowire="byType"/>
</beans>
setterBean的autowire设置的是byType,即按setter方法的参数类型自动注入,SetterBean的setService的类型是Service,而Service类有2个实现类:ServiceImpl1和ServiceImpl2,而容器容器中刚好有这2个实现类的bean:impl1和impl2,所以上面代码会报错,不知道注入的时候选择那个对象注入。我们可以通过primary属性来指定一个主要的bean,当从容器中查找的时候,如果有多个候选的bean符合查找的类型,此时容器将返回primary=”true”的bean对象。
spring还有一种方法也可以解决这个问题,可以设置某个bean是否在自动注入的时候是否为作为候选bean,通过bean元素的autowire-candidate属性类配置,如:
<bean id="impl1" class="com.chen.chat.chen.ServiceImpl1" autowire-candidate="false"/>
autowire-candidate:设置当前bean在被其他对象作为自动注入对象的时候,是否作为候选bean,默认值是true。
上面的过程:
容器在创建setterBean的时候,发现其autowire为byType,即按类型自动注入,此时会在SetterBean类中查找所有setter方法列表,其中就包含了setService方法,setService方法参数类型是Service,然后就会去容器中按照Service类型查找所有符合条件的bean列表,此时容器中会返回满足Service这种类型并且autowire-candidate=”true”的bean,bean元素的autowire-candidate的默认值是true,所以容器中符合条件的候选bean有2个:impl1和impl2,setService方法只需要一个满足条件的bean,此时会再去看这个列表中是否只有一个主要的bean(即bean元素的primary=“ture”的bean),而bean元素的primary默认值都是false,所以没有primary为true的bean,此时spring容器不知道选哪个了,此时就报错了,抛出NoUniqueBeanDefinitionException异常
上面我们把impl1的autowire-candidate改为false之后,运行看看:
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
System.out.println(context.getBean(Service.class));
}
}
com.chen.chat.chen.ServiceImpl2@2b552920
如果我们在SetterBean修改一下:
public class SetterBean {
private List<Service> serviceList;
public List<Service> getServiceList() {
return serviceList;
}
public void setServiceList(List<Service> serviceList) {
this.serviceList = serviceList;
}
@Override
public String toString() {
return "SetterBean{" +
"serviceList=" + serviceList +
'}';
}
}
上面的setServiceList(List serviceList)输出的是容器中的impl1,还是impl2,还是2个都有呢?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="impl1" class="com.chen.chat.chen.ServiceImpl1" autowire-candidate="false"/>
<bean id="impl2" class="com.chen.chat.chen.ServiceImpl2"/>
<bean id="setterBean" class="com.chen.chat.chen.SetterBean" autowire="byType"/>
</beans>
1,当 id=“impl1” 的 autowire-candidate=“false” 时
SetterBean{serviceList=[com.chen.chat.chen.ServiceImpl2@184f6be2]}
只注入了一个
1,当 id=“impl1” 的 autowire-candidate=“true” 时
SetterBean{serviceList=[com.chen.chat.chen.ServiceImpl1@184f6be2, com.chen.chat.chen.ServiceImpl2@56aac163]}
2个都有