抛出问题
spring启动中NoUniqueBeanDefinitionException异常是开发人员经常碰到的异常之一。出现NoUniqueBeanDefinitionException 一般的做法有:1、使用Qualifier 注解明确bean 2、指定一个bean为primary bean来解决。
但是在实际中碰到了一个意外,如下代码:
public class User {
Long id;
String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
}
public class VipUser extends User{
public VipUser(Long id, String name) {
super(id, name);
}
}
@Bean
public User user(){
return new User(1L,"张三");
}
@Bean
public VipUser vipUser(){
return new VipUser(2L, "李四");
}
public class DependecyDescribleTest {
@Autowired
User user;
public static void main(String[] args) {
........
}
}
启动后,User类型的bean 注册了两个,一个是name为user的User对象,一个是name为vipUser的VipUser对象。 在启动类中自动装配 User user。项目中没有@Primary进行注解,也没有使用@Qualifier ,按照刻板印象,那么应该会抛出NoUniqueBeanDefinitionException,实际情况是
,实际情况是运行正常。 同理将User user 改为User vipUser也运行正常, 而改为User user1 则抛出异常NoUniqueBeanDefinitionException。这说明在 @Autowired进行装配时,能根据字段名称就行装配。
spring源码解读(版本5.3.15)
我们先不考虑@Autowired 装配的具体细节。Autowired的最终装配会调用到DefaultListableBeanFactory的resolveDependency 方法。resolveDependency 会根据依赖描述返回合适的bean对象,这是spring factory中非常重要的一个方法。
/**
* 通过此工厂中定义的 bean 解决指定的依赖关系。
*/
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException{
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
拿上面的例子看,我们请求的对象不是Optional类型、不是ObjectFactory对象也不是javax.inject.Provider对象,最终会调用doResolveDependency 方法。
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
//shortcut翻译为捷径,这里我认为是缓存。 解析过的DependencyDescriptor 通过一些缓存的方式,避免下一次的解析。
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
Class> type = descriptor.getDependencyType();
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
// 解析的descriptor是一个数组、容器的描述,调用
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// 查找符合依赖描述符的候选者,返回matchingBeans 的key 是候选者beanName,value 可能是bean对象也可能是class。
Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
//如果候选者有多个
if (matchingBeans.size() > 1) {
//决定有没有合适的候选者。
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
//如果上边的方法决定不了一个后选择,那么抛出异常
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// We have exactly one match.
Map.Entry entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
doResolveDependency方法的代码比较多,做了一些简单注释。真正决定依赖描述符的返回值的是determineAutowireCandidate方法。
protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
Class> requiredType = descriptor.getDependencyType();
//从候选者中查找primary 的候选者
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
// 如果没有后选择,那么查看是否候选者有javax.annotation.Priority注解的,并返回权重最大者
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// 最后的兜底(应为:fallback 意思为退让),如果后选择的名字和依赖描述符中依赖的名字相同,那么可以返回候选者
for (Map.Entry entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
从上面的代码分析,大概理解了autowire选择候选者的过程 1、primary 2、是否存在javax.annotation.Priority 注解的候选者 3、名字相同。
读到这里可能还有个疑问,那么文章开头里说过可以使用Qualifier,Qualifier是如何工作的呢? findAutowireCandidates 方法里自己去找吧。