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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user