Autowiring of generic types

Includes revisions of MethodParameter and DependencyDescriptor (in particular towards a reference to the containing class). Also refines several ResolvableType method signatures.

Issue: SPR-9965
This commit is contained in:
Juergen Hoeller
2013-10-15 00:11:23 +02:00
parent 74794190a5
commit 3bd6dfe047
19 changed files with 874 additions and 351 deletions

View File

@@ -51,7 +51,6 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
@@ -471,14 +470,15 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = descriptor;
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
@@ -544,10 +544,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
descriptors[i] = new DependencyDescriptor(methodParam, this.required);
Object arg = beanFactory.resolveDependency(
descriptors[i], beanName, autowiredBeanNames, typeConverter);
DependencyDescriptor desc = new DependencyDescriptor(methodParam, this.required);
desc.setContainingClass(bean.getClass());
descriptors[i] = desc;
Object arg = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;

View File

@@ -24,12 +24,11 @@ import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
@@ -52,14 +51,12 @@ import org.springframework.util.StringUtils;
* @see Qualifier
* @see Value
*/
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<Class<? extends Annotation>>();
private Class<? extends Annotation> valueAnnotationType = Value.class;
private BeanFactory beanFactory;
/**
* Create a new QualifierAnnotationAutowireCandidateResolver
@@ -126,15 +123,6 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
this.valueAnnotationType = valueAnnotationType;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
/**
* Determine whether the provided bean definition is an autowire candidate.
@@ -150,21 +138,16 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
*/
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
// if explicitly false, do not proceed with qualifier check
return false;
}
if (descriptor == null) {
// no qualification necessary
return true;
}
boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class.equals(method.getReturnType())) {
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
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.equals(method.getReturnType())) {
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
}
@@ -244,8 +227,8 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
}
if (targetAnnotation == null) {
// look for matching annotation on the target class
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (getBeanFactory() != null) {
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
@@ -340,14 +323,4 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
return value;
}
/**
* This implementation always returns {@code null},
* leaving lazy resolution support up to subclasses.
*/
@Override
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
return null;
}
}

View File

@@ -25,8 +25,10 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
@@ -46,6 +48,8 @@ public class DependencyDescriptor implements Serializable {
private Class<?> declaringClass;
private Class<?> containingClass;
private String methodName;
private Class[] parameterTypes;
@@ -84,6 +88,7 @@ public class DependencyDescriptor implements Serializable {
Assert.notNull(methodParameter, "MethodParameter must not be null");
this.methodParameter = methodParameter;
this.declaringClass = methodParameter.getDeclaringClass();
this.containingClass = methodParameter.getContainingClass();
if (this.methodParameter.getMethod() != null) {
this.methodName = methodParameter.getMethod().getName();
this.parameterTypes = methodParameter.getMethod().getParameterTypes();
@@ -130,6 +135,7 @@ public class DependencyDescriptor implements Serializable {
this.methodParameter = (original.methodParameter != null ? new MethodParameter(original.methodParameter) : null);
this.field = original.field;
this.declaringClass = original.declaringClass;
this.containingClass = original.containingClass;
this.methodName = original.methodName;
this.parameterTypes = original.parameterTypes;
this.parameterIndex = original.parameterIndex;
@@ -186,6 +192,26 @@ public class DependencyDescriptor implements Serializable {
}
}
/**
* Optionally set the concrete class that contains this dependency.
* This may differ from the class that declares the parameter/field in that
* it may be a subclass thereof, potentially substituting type variables.
*/
public void setContainingClass(Class<?> containingClass) {
this.containingClass = containingClass;
if (this.methodParameter != null) {
GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass);
}
}
/**
* Build a ResolvableType object for the wrapped parameter/field.
*/
public ResolvableType getResolvableType() {
return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
ResolvableType.forMethodParameter(this.methodParameter));
}
/**
* Initialize parameter name discovery for the underlying method parameter, if any.
* <p>This method does not actually try to retrieve the parameter name at

View File

@@ -842,7 +842,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (type.isArray()) {
Class<?> componentType = type.getComponentType();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
targetDesc.increaseNestingLevel();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, targetDesc);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor);
@@ -867,7 +869,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor);
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
targetDesc.increaseNestingLevel();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, targetDesc);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor);
@@ -900,7 +904,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
targetDesc.increaseNestingLevel();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, targetDesc);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.support;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
/**
* Basic {@link AutowireCandidateResolver} that performs a full generic type
* match with the candidate's type if the dependency is declared as a generic type
* (e.g. Repository&lt;Customer&gt;).
*
* <p>This is the base class for
* {@link org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver},
* providing an implementation all non-annotation-based resolution steps at this level.
*
* @author Juergen Hoeller
* @since 4.0
*/
public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
// if explicitly false, do not proceed with any other checks
return false;
}
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor.getResolvableType()));
}
/**
* 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) {
// 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) {
// 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());
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
}
}
// 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 (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
}
return (targetType == null || dependencyType.isAssignableFrom(targetType));
}
/**
* This implementation always returns {@code null},
* leaving suggested value support up to subclasses.
*/
@Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
/**
* This implementation always returns {@code null},
* leaving lazy resolution support up to subclasses.
*/
@Override
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
return null;
}
}