当一个接口有2个不同实现时,使用@Autowired注解时会报
org.springframework.beans.factory.NoUniqueBeanDefinitionException异常信息
因为@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类。
解决方案:
1.用@Qualifier这个注解来解决问题
2.@Primary
Primary可以理解为默认优先选择,且不可以同时设置超过1个
内部实质是设置BeanDefinition的primary属性
源码解析:
在创建bean的时候,我们会获取bean的依赖bean,这个方法就是DefaultListableBeanFactory的doResolveDependency方法,如果匹配的bean大于1,会执行如下方法:
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
String autowiredBeanName;
try {
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
Object var20 = shortcut;
return var20;
}
Class<?> type = descriptor.getDependencyType();
Object value = this.getAutowireCandidateResolver().getSuggestedValue(descriptor);
Object var23;
if (value != null) {
if (value instanceof String) {
String strVal = this.resolveEmbeddedValue((String)value);
BeanDefinition bd = beanName != null && this.containsBean(beanName) ? this.getMergedBeanDefinition(beanName) : null;
value = this.evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = typeConverter != null ? typeConverter : this.getTypeConverter();
try {
var23 = converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
return var23;
} catch (UnsupportedOperationException var18) {
Object var25 = descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter());
return var25;
}
}
Object multipleBeans = this.resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
var23 = multipleBeans;
return var23;
}
Map<String, Object> matchingBeans = this.findAutowireCandidates(beanName, type, descriptor);
if (!matchingBeans.isEmpty()) {
Object instanceCandidate;
Object result;
//如果匹配的bean大于1,执行determineAutowireCandidate方法
if (matchingBeans.size() > 1) {
autowiredBeanName = this.determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (!this.isRequired(descriptor) && this.indicatesMultipleBeans(type)) {
result = null;
return result;
}
result = descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
return result;
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
Entry<String, Object> entry = (Entry)matchingBeans.entrySet().iterator().next();
autowiredBeanName = (String)entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
result = instanceCandidate;
if (instanceCandidate instanceof NullBean) {
if (this.isRequired(descriptor)) {
this.raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
Object var14 = result;
return var14;
}
if (this.isRequired(descriptor)) {
this.raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
autowiredBeanName = null;
} finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
return autowiredBeanName;
}
下面进入到determineAutowireCandidate方法中:
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String primaryCandidate = this.determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
} else {
String priorityCandidate = this.determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
} else {
Iterator var6 = candidates.entrySet().iterator();
String candidateName;
Object beanInstance;
do {
if (!var6.hasNext()) {
return null;
}
Entry<String, Object> entry = (Entry)var6.next();
candidateName = (String)entry.getKey();
beanInstance = entry.getValue();
} while((beanInstance == null || !this.resolvableDependencies.containsValue(beanInstance)) && !this.matchesBeanName(candidateName, descriptor.getDependencyName()));
return candidateName;
}
}
}
然后进入到determinePrimaryCandidate方法中:
@Nullable
protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
String primaryBeanName = null;
Iterator var4 = candidates.entrySet().iterator();
while(var4.hasNext()) {
Entry<String, Object> entry = (Entry)var4.next();
String candidateBeanName = (String)entry.getKey();
Object beanInstance = entry.getValue();
if (this.isPrimary(candidateBeanName, beanInstance)) {
if (primaryBeanName != null) {
boolean candidateLocal = this.containsBeanDefinition(candidateBeanName);
boolean primaryLocal = this.containsBeanDefinition(primaryBeanName);
//isPrimary方法是判断是否有@Primary注解,如果有多个满足条件的bean有@Primary注解就会抛出异常:
if (candidateLocal && primaryLocal) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(), "more than one 'primary' bean found among candidates: " + candidates.keySet());
}
if (candidateLocal) {
primaryBeanName = candidateBeanName;
}
} else {
primaryBeanName = candidateBeanName;
}
}
}
return primaryBeanName;
}
例子:
public interface Singer {
String sing(String lyrics);
}
有下面的两个实现类:
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
//注意,这里没有注解
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
下面就是注入上面的
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
private Singer singer;
public String sing(){
return singer.sing("song lyrics");
}
}
I am singing with DIO voice: song lyrics. 原因很简单,就是 OperaSinger 这个类上面根本没有加上注解@Copmonent 或者 @Service, 所以spring 注入的时候,只能找到 MetalSinger 这个实现类. 所以才有这个结果。
但是如果一旦 OperaSinger 这个类加上了@Copmonent 或者 @Service 注解,有趣的事情就会发生,你会发现一个错误的结果或异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger
提示很明确了,spring 根据类型无法选择到底注入哪一个。这个时候@Primay 可以闪亮登场了。
@Primary
@Component
public class OperaSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
如果代码改成这样,再次运行,结果如下:
“I am singing in Bocelli voice: song lyrics”, 用@Primary 告诉spring 在犹豫的时候优先选择哪一个具体的实现。
二、用@Qualifier这个注解来解决问题
将上面的两个类改为如下:
@Component // 加注解,让spring识别
@Qualifier("metalSinger")
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
@Component
@Qualifier("opreaSinger")
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
private Singer singer;
@Qualifier("opreaSinger")
public String sing(){
return singer.sing("song lyrics");
}
}
参考:https://blog.csdn.net/lz710117239/article/details/81192761