我是靠谱客的博主 轻松往事,最近开发中收集的这篇文章主要介绍SpringBoot 参数无法自动注入Map、Collection、数组问题SpringBoot 参数无法自动注入Map、Collection、数组问题,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

SpringBoot 参数无法自动注入Map、Collection、数组问题

 

问题描述:

    @Bean
    @ConditionalOnMissingBean(CpfDataSourceLookup.class)
    public CpfDataSourceLookup cpfDataSourceLookup(@Qualifier("backupDataSources") Map<String,Datasource> backupDataSources){
        return new CpfDataSourceLookup(backupDataSources);
    }
<util:map id="backupDataSources">
	</util:map>

这里我希望匹配一个Map类型的参数,并且我指定了Qualifier的名称,所以按道理来说是应该要自动注入的,但实际发现在注入时报找不到该类型。

springboot版本是1.5.17,同时我有个springboot2.3.1的工程,跑了一下发现是可以的。

这时候就来看下spring代码实现,代码位于DefaultListableBeanFactory.findAutowireCandidates方法

        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());//取出容器中所需类型所有bean名称(这里是Map类型所有bean)
		Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
		for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
			if (autowiringType.isAssignableFrom(requiredType)) {
				Object autowiringValue = this.resolvableDependencies.get(autowiringType);
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}
        //逐一匹配容器中每个Map类型的Bean是否匹配所需类型
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
        //还没匹配上并且不为数组、Collection、Map类型时进入
		if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
			// Consider fallback matches if the first pass failed to find anything...
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
            //没匹配上检查是否是自身bean引用
			if (result.isEmpty()) {
				// Consider self references as a final pass...
				// but in the case of a dependency collection, not the very same bean itself.
				for (String candidate : candidateNames) {
					if (isSelfReference(beanName, candidate) &&
							(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
							isAutowireCandidate(candidate, fallbackDescriptor)) {
						addCandidateEntry(result, candidate, descriptor, requiredType);
					}
				}
			}
		}
进入到第一次匹配的isAutowireCandidate方法中,为省篇幅直接进入最终判断方法中

最终会执行到
if (checkGenerics) {
    ResolvableType[] ourGenerics = this.getGenerics();//取出所需注入的Map范形[String,Datasource]
    ResolvableType[] typeGenerics = other.as(ourResolved).getGenerics();//取出容器中Map的范形[?,?]
    //比较元素数量是否一致
    if (ourGenerics.length != typeGenerics.length) {
        return false;
    }
    if (matchedBefore == null) {
        matchedBefore = new IdentityHashMap(1);
    }

    ((Map)matchedBefore).put(this.type, other.type);
    //逐一比较参数类型,注入的Map中的每个范形类是否为容器中对应位置范形类的父类(String和Datasource是否都为?,?的父类)
    for(int i = 0; i < ourGenerics.length; ++i) {
        if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], (Map)matchedBefore)) {
            return false;
        }
    }
}

从这里可以看出,因为在xml中声明的Map没有范形,而引用参数上声明了范形,而显然这代表不能进行向下转型

public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
		if (match && descriptor != null) {
			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
			if (match) {
				MethodParameter methodParam = descriptor.getMethodParameter();
				if (methodParam != null) {
					Method method = methodParam.getMethod();
					if (method == null || void.class == method.getReturnType()) {
						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
					}
				}
			}
		}
		return match;
	}

在匹配类型结束后,只有当类型匹配完成后,在匹配Qualifier注解,也就是说类型要是匹配不上,qualifier不生效

到这实际上问题已经可以解决:即注入参数Map加上@Qualifier注解但不要加范形,注意范型不明确时最好要加Qualifier匹配名称,因为在不匹配Qualifier时会列举出容器中所有Map,然后依次按照:使用@Primary指定@Priority优先级最高参数名称匹配三种方式确定最终注入对象。

到这springboot2和1的实现逻辑是一样的,也就是都不匹配,而为什么2最终能匹配上?

继续看下面的,发现有个差异

springboot1版本

.......
//还没匹配上并且不为数组、Collection、Map类型时进入
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
	// Consider fallback matches if the first pass failed to find anything...
	DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
	for (String candidate : candidateNames) {
		if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidatefallbackDescriptor)) {
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}
          //没匹配上检查是否是自身bean引用
	if (result.isEmpty()) {
		// Consider self references as a final pass...
		// but in the case of a dependency collection, not the very same bean itself.
		for (String candidate : candidateNames) {
			if (isSelfReference(beanName, candidate) &&
					(!(descriptor instanceof MultiElementDescriptor) |!beanName.equals(candidate)) &&
					isAutowireCandidate(candidate, fallbackDescriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
	}
}

springboot2

if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
	if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
			(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
		addCandidateEntry(result, candidate, descriptor, requiredType);
	}
}
if (result.isEmpty() && !multiple) {
	// Consider self references as a final pass...
	// but in the case of a dependency collection, not the very same bean itself.
	for (String candidate : candidateNames) {
		if (isSelfReference(beanName, candidate) &&
				(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
				isAutowireCandidate(candidate, fallbackDescriptor)) {
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}
}
private boolean indicatesMultipleBeans(Class<?> type) {
		return (type.isArray() || (type.isInterface() &&
				(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
	}

可以看出1中类型匹配失败后,后续匹配不支持数组、集合、Map类型;2中类型匹配失败后还可以对这几种类型匹配注解Qualifier,所以在2中参数加上注解Qualifier指定注入bean的名称即可

最后

以上就是轻松往事为你收集整理的SpringBoot 参数无法自动注入Map、Collection、数组问题SpringBoot 参数无法自动注入Map、Collection、数组问题的全部内容,希望文章能够帮你解决SpringBoot 参数无法自动注入Map、Collection、数组问题SpringBoot 参数无法自动注入Map、Collection、数组问题所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(30)

评论列表共有 0 条评论

立即
投稿
返回
顶部