概述
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、数组问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复