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