Accept non-generic type match as a fallback

DefaultListableBeanFactory performs a fallback check for autowire candidates now, which GenericTypeAwareAutowireCandidateResolver implements to accept raw type matches if the target class has unresolvable type variables. Full generic matches are still preferred; the BeanFactory will only start looking for fallback matches if the first pass led to an empty result.

Issue: SPR-10993
Issue: SPR-11004
This commit is contained in:
Juergen Hoeller
2013-10-25 17:52:38 +02:00
parent 02f9b713b0
commit 0851766738
8 changed files with 332 additions and 122 deletions

View File

@@ -212,6 +212,29 @@ public class DependencyDescriptor implements Serializable {
ResolvableType.forMethodParameter(this.methodParameter));
}
/**
* Return whether a fallback match is allowed.
* <p>This is {@code false} by default but may be overridden to return {@code true} in order
* to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
* that a fallback match is acceptable as well.
*/
public boolean fallbackMatchAllowed() {
return false;
}
/**
* Return a variant of this descriptor that is intended for a fallback match.
* @see #fallbackMatchAllowed()
*/
public DependencyDescriptor forFallbackMatch() {
return new DependencyDescriptor(this) {
@Override
public boolean fallbackMatchAllowed() {
return true;
}
};
}
/**
* Initialize parameter name discovery for the underlying method parameter, if any.
* <p>This method does not actually try to retrieve the parameter name at
@@ -241,7 +264,8 @@ public class DependencyDescriptor implements Serializable {
if (this.nestingLevel > 1) {
Type type = this.field.getGenericType();
if (type instanceof ParameterizedType) {
Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Type arg = args[args.length - 1];
if (arg instanceof Class) {
return (Class) arg;
}

View File

@@ -979,6 +979,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
result.put(candidateName, getBean(candidateName));
}
}
if (result.isEmpty()) {
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidateName : candidateNames) {
if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
result.put(candidateName, getBean(candidateName));
}
}
}
return result;
}

View File

@@ -57,24 +57,28 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
// if explicitly false, do not proceed with any other checks
return false;
}
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor.getResolvableType()));
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
}
/**
* Match the given dependency type with its generic type information
* against the given candidate bean definition.
*/
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, ResolvableType dependencyType) {
if (dependencyType.getType() instanceof Class) {
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
ResolvableType dependencyType = descriptor.getResolvableType();
if (!dependencyType.hasGenerics()) {
// No generic type -> we know it's a Class type-match, so no need to check again.
return true;
}
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
ResolvableType targetType = null;
if (bd.getResolvedFactoryMethod() != null) {
RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
if (rbd != null && rbd.getResolvedFactoryMethod() != null) {
// Should typically be set for any kind of factory method, since the BeanFactory
// pre-resolves them before reaching out to the AutowireCandidateResolver...
targetType = ResolvableType.forMethodReturnType(bd.getResolvedFactoryMethod());
targetType = ResolvableType.forMethodReturnType(rbd.getResolvedFactoryMethod());
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
@@ -86,14 +90,20 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
}
// Fallback: no BeanFactory set, or no type resolvable through it
// -> best-effort match against the target class if applicable.
if (targetType == null && bd.hasBeanClass() && bd.getFactoryMethodName() == null) {
Class<?> beanClass = bd.getBeanClass();
if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
Class<?> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
}
return (targetType == null || dependencyType.isAssignableFrom(targetType));
if (targetType == null) {
return true;
}
if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
return descriptor.getDependencyType().isAssignableFrom(targetType.getRawClass());
}
return dependencyType.isAssignableFrom(targetType);
}