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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1145,6 +1145,166 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedFieldInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBean.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
StringRepository sr = new StringRepository();
|
||||
bf.registerSingleton("stringRepo", sr);
|
||||
IntegerRepository ir = new IntegerRepository();
|
||||
bf.registerSingleton("integerRepo", ir);
|
||||
|
||||
RepositoryFieldInjectionBean bean = (RepositoryFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(sr, bean.stringRepository);
|
||||
assertSame(ir, bean.integerRepository);
|
||||
assertSame(1, bean.stringRepositoryArray.length);
|
||||
assertSame(1, bean.integerRepositoryArray.length);
|
||||
assertSame(sr, bean.stringRepositoryArray[0]);
|
||||
assertSame(ir, bean.integerRepositoryArray[0]);
|
||||
assertSame(1, bean.stringRepositoryList.size());
|
||||
assertSame(1, bean.integerRepositoryList.size());
|
||||
assertSame(sr, bean.stringRepositoryList.get(0));
|
||||
assertSame(ir, bean.integerRepositoryList.get(0));
|
||||
assertSame(1, bean.stringRepositoryMap.size());
|
||||
assertSame(1, bean.integerRepositoryMap.size());
|
||||
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
|
||||
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedFieldInjectionWithSubstitutedVariables() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSubstitutedVariables.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
StringRepository sr = new StringRepository();
|
||||
bf.registerSingleton("stringRepo", sr);
|
||||
IntegerRepository ir = new IntegerRepository();
|
||||
bf.registerSingleton("integerRepo", ir);
|
||||
|
||||
RepositoryFieldInjectionBeanWithSubstitutedVariables bean = (RepositoryFieldInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean");
|
||||
assertSame(sr, bean.stringRepository);
|
||||
assertSame(ir, bean.integerRepository);
|
||||
assertSame(1, bean.stringRepositoryArray.length);
|
||||
assertSame(1, bean.integerRepositoryArray.length);
|
||||
assertSame(sr, bean.stringRepositoryArray[0]);
|
||||
assertSame(ir, bean.integerRepositoryArray[0]);
|
||||
assertSame(1, bean.stringRepositoryList.size());
|
||||
assertSame(1, bean.integerRepositoryList.size());
|
||||
assertSame(sr, bean.stringRepositoryList.get(0));
|
||||
assertSame(ir, bean.integerRepositoryList.get(0));
|
||||
assertSame(1, bean.stringRepositoryMap.size());
|
||||
assertSame(1, bean.integerRepositoryMap.size());
|
||||
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
|
||||
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedMethodInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBean.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
StringRepository sr = new StringRepository();
|
||||
bf.registerSingleton("stringRepo", sr);
|
||||
IntegerRepository ir = new IntegerRepository();
|
||||
bf.registerSingleton("integerRepo", ir);
|
||||
|
||||
RepositoryMethodInjectionBean bean = (RepositoryMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(sr, bean.stringRepository);
|
||||
assertSame(ir, bean.integerRepository);
|
||||
assertSame(1, bean.stringRepositoryArray.length);
|
||||
assertSame(1, bean.integerRepositoryArray.length);
|
||||
assertSame(sr, bean.stringRepositoryArray[0]);
|
||||
assertSame(ir, bean.integerRepositoryArray[0]);
|
||||
assertSame(1, bean.stringRepositoryList.size());
|
||||
assertSame(1, bean.integerRepositoryList.size());
|
||||
assertSame(sr, bean.stringRepositoryList.get(0));
|
||||
assertSame(ir, bean.integerRepositoryList.get(0));
|
||||
assertSame(1, bean.stringRepositoryMap.size());
|
||||
assertSame(1, bean.integerRepositoryMap.size());
|
||||
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
|
||||
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedMethodInjectionWithSubstitutedVariables() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBeanWithSubstitutedVariables.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
StringRepository sr = new StringRepository();
|
||||
bf.registerSingleton("stringRepo", sr);
|
||||
IntegerRepository ir = new IntegerRepository();
|
||||
bf.registerSingleton("integerRepo", ir);
|
||||
|
||||
RepositoryMethodInjectionBeanWithSubstitutedVariables bean = (RepositoryMethodInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean");
|
||||
assertSame(sr, bean.stringRepository);
|
||||
assertSame(ir, bean.integerRepository);
|
||||
assertSame(1, bean.stringRepositoryArray.length);
|
||||
assertSame(1, bean.integerRepositoryArray.length);
|
||||
assertSame(sr, bean.stringRepositoryArray[0]);
|
||||
assertSame(ir, bean.integerRepositoryArray[0]);
|
||||
assertSame(1, bean.stringRepositoryList.size());
|
||||
assertSame(1, bean.integerRepositoryList.size());
|
||||
assertSame(sr, bean.stringRepositoryList.get(0));
|
||||
assertSame(ir, bean.integerRepositoryList.get(0));
|
||||
assertSame(1, bean.stringRepositoryMap.size());
|
||||
assertSame(1, bean.integerRepositoryMap.size());
|
||||
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
|
||||
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedConstructorInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
StringRepository sr = new StringRepository();
|
||||
bf.registerSingleton("stringRepo", sr);
|
||||
IntegerRepository ir = new IntegerRepository();
|
||||
bf.registerSingleton("integerRepo", ir);
|
||||
|
||||
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(sr, bean.stringRepository);
|
||||
assertSame(ir, bean.integerRepository);
|
||||
assertSame(1, bean.stringRepositoryArray.length);
|
||||
assertSame(1, bean.integerRepositoryArray.length);
|
||||
assertSame(sr, bean.stringRepositoryArray[0]);
|
||||
assertSame(ir, bean.integerRepositoryArray[0]);
|
||||
assertSame(1, bean.stringRepositoryList.size());
|
||||
assertSame(1, bean.integerRepositoryList.size());
|
||||
assertSame(sr, bean.stringRepositoryList.get(0));
|
||||
assertSame(ir, bean.integerRepositoryList.get(0));
|
||||
assertSame(1, bean.stringRepositoryMap.size());
|
||||
assertSame(1, bean.integerRepositoryMap.size());
|
||||
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
|
||||
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
|
||||
}
|
||||
|
||||
|
||||
public static class ResourceInjectionBean {
|
||||
|
||||
@@ -1689,4 +1849,235 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
public static class FixedOrder2NestedTestBean extends NestedTestBean {
|
||||
}
|
||||
|
||||
|
||||
public interface Repository<T> {
|
||||
}
|
||||
|
||||
public static class StringRepository implements Repository<String> {
|
||||
}
|
||||
|
||||
public static class IntegerRepository implements Repository<Integer> {
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryFieldInjectionBean {
|
||||
|
||||
@Autowired
|
||||
public Repository<String> stringRepository;
|
||||
|
||||
@Autowired
|
||||
public Repository<Integer> integerRepository;
|
||||
|
||||
@Autowired
|
||||
public Repository<String>[] stringRepositoryArray;
|
||||
|
||||
@Autowired
|
||||
public Repository<Integer>[] integerRepositoryArray;
|
||||
|
||||
@Autowired
|
||||
public List<Repository<String>> stringRepositoryList;
|
||||
|
||||
@Autowired
|
||||
public List<Repository<Integer>> integerRepositoryList;
|
||||
|
||||
@Autowired
|
||||
public Map<String, Repository<String>> stringRepositoryMap;
|
||||
|
||||
@Autowired
|
||||
public Map<String, Repository<Integer>> integerRepositoryMap;
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryFieldInjectionBeanWithVariables<S, I> {
|
||||
|
||||
@Autowired
|
||||
public Repository<S> stringRepository;
|
||||
|
||||
@Autowired
|
||||
public Repository<I> integerRepository;
|
||||
|
||||
@Autowired
|
||||
public Repository<S>[] stringRepositoryArray;
|
||||
|
||||
@Autowired
|
||||
public Repository<I>[] integerRepositoryArray;
|
||||
|
||||
@Autowired
|
||||
public List<Repository<S>> stringRepositoryList;
|
||||
|
||||
@Autowired
|
||||
public List<Repository<I>> integerRepositoryList;
|
||||
|
||||
@Autowired
|
||||
public Map<String, Repository<S>> stringRepositoryMap;
|
||||
|
||||
@Autowired
|
||||
public Map<String, Repository<I>> integerRepositoryMap;
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryFieldInjectionBeanWithSubstitutedVariables
|
||||
extends RepositoryFieldInjectionBeanWithVariables<String, Integer> {
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryMethodInjectionBean {
|
||||
|
||||
public Repository<String> stringRepository;
|
||||
|
||||
public Repository<Integer> integerRepository;
|
||||
|
||||
public Repository<String>[] stringRepositoryArray;
|
||||
|
||||
public Repository<Integer>[] integerRepositoryArray;
|
||||
|
||||
public List<Repository<String>> stringRepositoryList;
|
||||
|
||||
public List<Repository<Integer>> integerRepositoryList;
|
||||
|
||||
public Map<String, Repository<String>> stringRepositoryMap;
|
||||
|
||||
public Map<String, Repository<Integer>> integerRepositoryMap;
|
||||
|
||||
@Autowired
|
||||
public void setStringRepository(Repository<String> stringRepository) {
|
||||
this.stringRepository = stringRepository;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepository(Repository<Integer> integerRepository) {
|
||||
this.integerRepository = integerRepository;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryArray(Repository<String>[] stringRepositoryArray) {
|
||||
this.stringRepositoryArray = stringRepositoryArray;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryArray(Repository<Integer>[] integerRepositoryArray) {
|
||||
this.integerRepositoryArray = integerRepositoryArray;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryList(List<Repository<String>> stringRepositoryList) {
|
||||
this.stringRepositoryList = stringRepositoryList;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryList(List<Repository<Integer>> integerRepositoryList) {
|
||||
this.integerRepositoryList = integerRepositoryList;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryMap(Map<String, Repository<String>> stringRepositoryMap) {
|
||||
this.stringRepositoryMap = stringRepositoryMap;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryMap(Map<String, Repository<Integer>> integerRepositoryMap) {
|
||||
this.integerRepositoryMap = integerRepositoryMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryMethodInjectionBeanWithVariables<S, I> {
|
||||
|
||||
public Repository<S> stringRepository;
|
||||
|
||||
public Repository<I> integerRepository;
|
||||
|
||||
public Repository<S>[] stringRepositoryArray;
|
||||
|
||||
public Repository<I>[] integerRepositoryArray;
|
||||
|
||||
public List<Repository<S>> stringRepositoryList;
|
||||
|
||||
public List<Repository<I>> integerRepositoryList;
|
||||
|
||||
public Map<String, Repository<S>> stringRepositoryMap;
|
||||
|
||||
public Map<String, Repository<I>> integerRepositoryMap;
|
||||
|
||||
@Autowired
|
||||
public void setStringRepository(Repository<S> stringRepository) {
|
||||
this.stringRepository = stringRepository;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepository(Repository<I> integerRepository) {
|
||||
this.integerRepository = integerRepository;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryArray(Repository<S>[] stringRepositoryArray) {
|
||||
this.stringRepositoryArray = stringRepositoryArray;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryArray(Repository<I>[] integerRepositoryArray) {
|
||||
this.integerRepositoryArray = integerRepositoryArray;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryList(List<Repository<S>> stringRepositoryList) {
|
||||
this.stringRepositoryList = stringRepositoryList;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryList(List<Repository<I>> integerRepositoryList) {
|
||||
this.integerRepositoryList = integerRepositoryList;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStringRepositoryMap(Map<String, Repository<S>> stringRepositoryMap) {
|
||||
this.stringRepositoryMap = stringRepositoryMap;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setIntegerRepositoryMap(Map<String, Repository<I>> integerRepositoryMap) {
|
||||
this.integerRepositoryMap = integerRepositoryMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryMethodInjectionBeanWithSubstitutedVariables
|
||||
extends RepositoryMethodInjectionBeanWithVariables<String, Integer> {
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryConstructorInjectionBean {
|
||||
|
||||
public Repository<String> stringRepository;
|
||||
|
||||
public Repository<Integer> integerRepository;
|
||||
|
||||
public Repository<String>[] stringRepositoryArray;
|
||||
|
||||
public Repository<Integer>[] integerRepositoryArray;
|
||||
|
||||
public List<Repository<String>> stringRepositoryList;
|
||||
|
||||
public List<Repository<Integer>> integerRepositoryList;
|
||||
|
||||
public Map<String, Repository<String>> stringRepositoryMap;
|
||||
|
||||
public Map<String, Repository<Integer>> integerRepositoryMap;
|
||||
|
||||
@Autowired
|
||||
public RepositoryConstructorInjectionBean(Repository<String> stringRepository, Repository<Integer> integerRepository,
|
||||
Repository<String>[] stringRepositoryArray, Repository<Integer>[] integerRepositoryArray,
|
||||
List<Repository<String>> stringRepositoryList, List<Repository<Integer>> integerRepositoryList,
|
||||
Map<String, Repository<String>> stringRepositoryMap, Map<String, Repository<Integer>> integerRepositoryMap) {
|
||||
this.stringRepository = stringRepository;
|
||||
this.integerRepository = integerRepository;
|
||||
this.stringRepositoryArray = stringRepositoryArray;
|
||||
this.integerRepositoryArray = integerRepositoryArray;
|
||||
this.stringRepositoryList = stringRepositoryList;
|
||||
this.integerRepositoryList = integerRepositoryList;
|
||||
this.stringRepositoryMap = stringRepositoryMap;
|
||||
this.integerRepositoryMap = integerRepositoryMap;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.context.annotation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
|
||||
import org.springframework.beans.factory.support.ChildBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -132,6 +135,25 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericsBasedInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryInjectionBean.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
bf.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(bf);
|
||||
|
||||
RepositoryInjectionBean bean = (RepositoryInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("stringRepo"), bean.stringRepository);
|
||||
assertSame(bf.getBean("integerRepo"), bean.integerRepository);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class SingletonBeanConfig {
|
||||
@@ -171,4 +193,35 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Repository<T> {
|
||||
}
|
||||
|
||||
|
||||
public static class RepositoryInjectionBean {
|
||||
|
||||
@Autowired
|
||||
public Repository<String> stringRepository;
|
||||
|
||||
@Autowired
|
||||
public Repository<Integer> integerRepository;
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
public static class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public Repository<String> stringRepo() {
|
||||
return new Repository<String>() {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Repository<Integer> integerRepo() {
|
||||
return new Repository<Integer>() {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
|
||||
* expressing the type index for traversal at that level
|
||||
* @return the generic type, or {@code null} if none
|
||||
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> getCollectionFieldType(Field collectionField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
@@ -132,7 +132,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
|
||||
* expressing the type index for traversal at that level
|
||||
* @return the generic type, or {@code null} if none
|
||||
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> getMapKeyFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
@@ -169,7 +169,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
|
||||
* expressing the type index for traversal at that level
|
||||
* @return the generic type, or {@code null} if none
|
||||
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> getMapValueFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
@@ -182,7 +182,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapKeyParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asMap().resolveGeneric(1);
|
||||
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +209,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getCollectionReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +223,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getCollectionReturnType(Method method, int nestingLevel) {
|
||||
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +232,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapKeyReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +244,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapKeyReturnType(Method method, int nestingLevel) {
|
||||
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +253,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(1);
|
||||
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,14 +265,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueReturnType(Method method, int nestingLevel) {
|
||||
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
private static ResolvableType forMethodParameter(MethodParameter methodParam) {
|
||||
if (methodParam.resolveClass != null) {
|
||||
return ResolvableType.forMethodParameter(methodParam, methodParam.resolveClass);
|
||||
}
|
||||
return ResolvableType.forMethodParameter(methodParam);
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 2.5.2
|
||||
* @see GenericCollectionTypeResolver
|
||||
@@ -52,7 +51,7 @@ public abstract class GenericTypeResolver {
|
||||
* Determine the target type for the given parameter specification.
|
||||
* @param methodParam the method parameter specification
|
||||
* @return the corresponding generic parameter type
|
||||
* @deprecated as of Spring 4.0 use {@link MethodParameter#getGenericParameterType()}
|
||||
* @deprecated as of Spring 4.0, use {@link MethodParameter#getGenericParameterType()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Type getTargetType(MethodParameter methodParam) {
|
||||
@@ -69,8 +68,8 @@ public abstract class GenericTypeResolver {
|
||||
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) {
|
||||
Assert.notNull(methodParam, "MethodParameter must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
methodParam.resolveClass = clazz;
|
||||
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam, clazz).resolve());
|
||||
methodParam.setContainingClass(clazz);
|
||||
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam).resolve());
|
||||
return methodParam.getParameterType();
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ public abstract class GenericTypeResolver {
|
||||
public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
return ResolvableType.forMethodReturn(method, clazz).resolve(method.getReturnType());
|
||||
return ResolvableType.forMethodReturnType(method, clazz).resolve(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,12 +112,14 @@ public abstract class GenericTypeResolver {
|
||||
* </ul>
|
||||
* @param method the method to introspect, never {@code null}
|
||||
* @param args the arguments that will be supplied to the method when it is
|
||||
* invoked, never {@code null}
|
||||
* invoked (never {@code null})
|
||||
* @param classLoader the ClassLoader to resolve class names against, if necessary
|
||||
* (may be {@code null})
|
||||
* @return the resolved target return type, the standard return type, or {@code null}
|
||||
* @since 3.2
|
||||
* @since 3.2.5
|
||||
* @see #resolveReturnType
|
||||
*/
|
||||
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
|
||||
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args, ClassLoader classLoader) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(args, "Argument array must not be null");
|
||||
|
||||
@@ -158,8 +159,18 @@ public abstract class GenericTypeResolver {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
for (Type typeArg : actualTypeArguments) {
|
||||
if (typeArg.equals(genericReturnType)) {
|
||||
if (args[i] instanceof Class) {
|
||||
return (Class<?>) args[i];
|
||||
Object arg = args[i];
|
||||
if (arg instanceof Class) {
|
||||
return (Class<?>) arg;
|
||||
}
|
||||
else if (arg instanceof String) {
|
||||
try {
|
||||
return classLoader.loadClass((String) arg);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not resolve specific class name argument [" + arg + "]", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Consider adding logic to determine the class of the typeArg, if possible.
|
||||
@@ -187,7 +198,7 @@ public abstract class GenericTypeResolver {
|
||||
*/
|
||||
public static Class<?> resolveReturnTypeArgument(Method method, Class<?> genericIfc) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
ResolvableType resolvableType = ResolvableType.forMethodReturn(method).as(genericIfc);
|
||||
ResolvableType resolvableType = ResolvableType.forMethodReturnType(method).as(genericIfc);
|
||||
if (!resolvableType.hasGenerics() || resolvableType.getType() instanceof WildcardType) {
|
||||
return null;
|
||||
}
|
||||
@@ -230,7 +241,7 @@ public abstract class GenericTypeResolver {
|
||||
*/
|
||||
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
|
||||
if(!type.hasGenerics()) {
|
||||
if (!type.hasGenerics()) {
|
||||
return null;
|
||||
}
|
||||
return type.resolveGenerics();
|
||||
@@ -261,16 +272,15 @@ public abstract class GenericTypeResolver {
|
||||
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
|
||||
if (typeVariableMap == null) {
|
||||
typeVariableMap = new HashMap<TypeVariable, Type>();
|
||||
buildTypeVaraibleMap(ResolvableType.forClass(clazz), typeVariableMap);
|
||||
buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap);
|
||||
typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap));
|
||||
}
|
||||
return typeVariableMap;
|
||||
}
|
||||
|
||||
private static void buildTypeVaraibleMap(ResolvableType type,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
if(type != ResolvableType.NONE) {
|
||||
if(type.getType() instanceof ParameterizedType) {
|
||||
private static void buildTypeVariableMap(ResolvableType type, Map<TypeVariable, Type> typeVariableMap) {
|
||||
if (type != ResolvableType.NONE) {
|
||||
if (type.getType() instanceof ParameterizedType) {
|
||||
TypeVariable<?>[] variables = type.resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
ResolvableType generic = type.getGeneric(i);
|
||||
@@ -282,94 +292,50 @@ public abstract class GenericTypeResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypeVaraibleMap(type.getSuperType(), typeVariableMap);
|
||||
buildTypeVariableMap(type.getSuperType(), typeVariableMap);
|
||||
for (ResolvableType interfaceType : type.getInterfaces()) {
|
||||
buildTypeVaraibleMap(interfaceType, typeVariableMap);
|
||||
buildTypeVariableMap(interfaceType, typeVariableMap);
|
||||
}
|
||||
if (type.resolve().isMemberClass()) {
|
||||
buildTypeVaraibleMap(ResolvableType.forClass(type.resolve().getEnclosingClass()),
|
||||
typeVariableMap);
|
||||
buildTypeVariableMap(ResolvableType.forClass(type.resolve().getEnclosingClass()), typeVariableMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the raw type for the given generic parameter type.
|
||||
* @param genericType the generic type to resolve
|
||||
* @param typeVariableMap the TypeVariable Map to resolved against
|
||||
* @return the resolved raw type
|
||||
*/
|
||||
@Deprecated
|
||||
static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type resolvedType = genericType;
|
||||
if (genericType instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) genericType;
|
||||
resolvedType = typeVariableMap.get(tv);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(tv);
|
||||
}
|
||||
}
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return ((ParameterizedType) resolvedType).getRawType();
|
||||
}
|
||||
else {
|
||||
return resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the bound {@code Type} for a given {@link TypeVariable}.
|
||||
*/
|
||||
@Deprecated
|
||||
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
|
||||
Type[] bounds = typeVariable.getBounds();
|
||||
if (bounds.length == 0) {
|
||||
return Object.class;
|
||||
}
|
||||
Type bound = bounds[0];
|
||||
if (bound instanceof TypeVariable) {
|
||||
bound = extractBoundForTypeVariable((TypeVariable) bound);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapts a {@code typeVariableMap} to a {@link TypeVariableResolver}.
|
||||
*/
|
||||
private static class TypeVariableMapResolver implements TypeVariableResolver {
|
||||
|
||||
private Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
public TypeVariableMapResolver(Map<TypeVariable, Type> typeVariableMap) {
|
||||
Assert.notNull("TypeVariableMap must not be null");
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type resolveVariable(TypeVariable typeVariable) {
|
||||
return typeVariableMap.get(typeVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return typeVariableMap.hashCode();
|
||||
return this.typeVariableMap.get(typeVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(obj == this) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if(obj instanceof TypeVariableMapResolver) {
|
||||
if (obj instanceof TypeVariableMapResolver) {
|
||||
TypeVariableMapResolver other = (TypeVariableMapResolver) obj;
|
||||
return this.typeVariableMap.equals(other.typeVariableMap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.typeVariableMap.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -48,6 +47,8 @@ public class MethodParameter {
|
||||
|
||||
private final int parameterIndex;
|
||||
|
||||
private Class<?> containingClass;
|
||||
|
||||
private Class<?> parameterType;
|
||||
|
||||
private Type genericParameterType;
|
||||
@@ -63,8 +64,6 @@ public class MethodParameter {
|
||||
/** Map from Integer level to Integer type index */
|
||||
Map<Integer, Integer> typeIndexesPerLevel;
|
||||
|
||||
Class<?> resolveClass;
|
||||
|
||||
private int hash = 0;
|
||||
|
||||
|
||||
@@ -130,6 +129,7 @@ public class MethodParameter {
|
||||
this.method = original.method;
|
||||
this.constructor = original.constructor;
|
||||
this.parameterIndex = original.parameterIndex;
|
||||
this.containingClass = original.containingClass;
|
||||
this.parameterType = original.parameterType;
|
||||
this.genericParameterType = original.genericParameterType;
|
||||
this.parameterAnnotations = original.parameterAnnotations;
|
||||
@@ -137,7 +137,6 @@ public class MethodParameter {
|
||||
this.parameterName = original.parameterName;
|
||||
this.nestingLevel = original.nestingLevel;
|
||||
this.typeIndexesPerLevel = original.typeIndexesPerLevel;
|
||||
this.resolveClass = original.resolveClass;
|
||||
this.hash = original.hash;
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ public class MethodParameter {
|
||||
/**
|
||||
* Return the class that declares the underlying Method or Constructor.
|
||||
*/
|
||||
public Class getDeclaringClass() {
|
||||
public Class<?> getDeclaringClass() {
|
||||
return getMember().getDeclaringClass();
|
||||
}
|
||||
|
||||
@@ -207,6 +206,17 @@ public class MethodParameter {
|
||||
return this.parameterIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a containing class to resolve the parameter type against.
|
||||
*/
|
||||
void setContainingClass(Class<?> containingClass) {
|
||||
this.containingClass = containingClass;
|
||||
}
|
||||
|
||||
public Class<?> getContainingClass() {
|
||||
return (this.containingClass != null ? this.containingClass : getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a resolved (generic) parameter type.
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* <p>{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields},
|
||||
* {@link #forMethodParameter(Method, int) method parameters},
|
||||
* {@link #forMethodReturn(Method) method returns}, {@link #forClass(Class) classes}, or
|
||||
* {@link #forMethodReturnType(Method) method returns}, {@link #forClass(Class) classes}, or
|
||||
* directly from a {@link #forType(Type) java.lang.reflect.Type}. Most methods on this class
|
||||
* will themselves return {@link ResolvableType}s, allowing easy navigation. For example:
|
||||
* <pre class="code">
|
||||
@@ -59,11 +59,12 @@ import org.springframework.util.StringUtils;
|
||||
* </pre>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see TypeVariableResolver
|
||||
* @see #forField(Field)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
* @see #forMethodReturn(Method)
|
||||
* @see #forMethodReturnType(Method)
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
* @see #forClass(Class)
|
||||
* @see #forType(Type)
|
||||
@@ -85,7 +86,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
|
||||
|
||||
/**
|
||||
* The underlying java type being managed (only ever {@code null} for {@link #NONE})
|
||||
* The underlying java type being managed (only ever {@code null} for {@link #NONE}).
|
||||
*/
|
||||
private final Type type;
|
||||
|
||||
@@ -127,8 +128,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* {@link Class#isAssignableFrom(Class) assignable from} the given {@code type} as
|
||||
* well as if all {@link #getGenerics() generics} are assignable.
|
||||
* @param type the type to be checked
|
||||
* @return {@code true} if the specified {@code type} can be assigned to this
|
||||
* {@code type}.
|
||||
* @return {@code true} if the specified {@code type} can be assigned to this {@code type}
|
||||
*/
|
||||
public boolean isAssignableFrom(ResolvableType type) {
|
||||
return isAssignableFrom(type, false);
|
||||
@@ -323,15 +323,12 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* of 1 indicates this type; 2 indicates the first nested generic; 3 the second; and so
|
||||
* on. For example, given {@code List<Set<Integer>>} level 1 refers to the
|
||||
* {@code List}, level 2 the {@code Set}, and level 3 the {@code Integer}.
|
||||
*
|
||||
* <p>The {@code typeIndexesPerLevel} map can be used to reference a specific generic
|
||||
* for the given level. For example, an index of 0 would refer to a {@code Map} key;
|
||||
* whereas, 1 would refer to the value. If the map does not contain a value for a
|
||||
* specific level the last generic will be used (e.g. a {@code Map} value).
|
||||
*
|
||||
* <p>Nesting levels may also apply to array types; for example given
|
||||
* {@code String[]}, a nesting level of 2 refers to {@code String}.
|
||||
*
|
||||
* <p>If a type does not {@link #hasGenerics() contain} generics the
|
||||
* {@link #getSuperType() supertype} hierarchy will be considered.
|
||||
* @param nestingLevel the required nesting level, indexed from 1 for the current
|
||||
@@ -340,8 +337,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* level (may be {@code null})
|
||||
* @return a {@link ResolvableType} for the nested level or {@link #NONE}
|
||||
*/
|
||||
public ResolvableType getNested(int nestingLevel,
|
||||
Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
public ResolvableType getNested(int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
ResolvableType result = this;
|
||||
for (int i = 2; i <= nestingLevel; i++) {
|
||||
if (result.isArray()) {
|
||||
@@ -369,7 +365,6 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* for example {@code getGeneric(1, 0)} will access the {@code String} from the nested
|
||||
* {@code List}. For convenience, if no indexes are specified the first generic is
|
||||
* returned.
|
||||
*
|
||||
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
|
||||
* @param indexes the indexes that refer to the generic parameter (may be omitted to
|
||||
* return the first generic)
|
||||
@@ -533,10 +528,8 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
public Type resolveVariable(TypeVariable<?> variable) {
|
||||
Assert.notNull("Variable must not be null");
|
||||
if (this.type instanceof ParameterizedType) {
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) this.type;
|
||||
Type owner = parameterizedType.getOwnerType();
|
||||
|
||||
if (parameterizedType.getRawType().equals(variable.getGenericDeclaration())) {
|
||||
TypeVariable<?>[] variables = resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
@@ -545,21 +538,18 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type resolved = null;
|
||||
if (this.variableResolver != null) {
|
||||
resolved = this.variableResolver.resolveVariable(variable);
|
||||
resolved = this.variableResolver.resolveVariable(variable);
|
||||
}
|
||||
if (resolved == null && owner != null) {
|
||||
resolved = forType(owner, this.variableResolver).resolveVariable(variable);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
if (this.type instanceof TypeVariable<?>) {
|
||||
if (this.type instanceof TypeVariable) {
|
||||
return resolveType().resolveVariable(variable);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -576,19 +566,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(resolve() == null ? "?" : resolve().getName());
|
||||
if (hasGenerics()) {
|
||||
result.append("<");
|
||||
result.append('<');
|
||||
result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", "));
|
||||
result.append(">");
|
||||
result.append('>');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.type) * 31
|
||||
+ ObjectUtils.nullSafeHashCode(this.variableResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
@@ -596,13 +580,17 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
if (obj instanceof ResolvableType) {
|
||||
ResolvableType other = (ResolvableType) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.type, other.type)
|
||||
&& ObjectUtils.nullSafeEquals(this.variableResolver,
|
||||
other.variableResolver);
|
||||
return ObjectUtils.nullSafeEquals(this.type, other.type) &&
|
||||
ObjectUtils.nullSafeEquals(this.variableResolver, other.variableResolver);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class}. For example
|
||||
@@ -628,8 +616,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forClass(Class<?> sourceClass, Class<?> implementationClass) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
ResolvableType asType = forType(implementationClass).as(sourceClass);
|
||||
ResolvableType asType = (implementationClass != null ? forType(implementationClass).as(sourceClass) : NONE);
|
||||
return (asType == NONE ? forType(sourceClass) : asType);
|
||||
}
|
||||
|
||||
@@ -646,8 +633,9 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation. Use this variant when the class that declares the field includes
|
||||
* generic parameter variables that are satisfied by the implementation class.
|
||||
* implementation.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
@@ -655,12 +643,43 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forField(Field field, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
field.getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with the
|
||||
* given nesting level.
|
||||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
return forType(field.getGenericType()).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation and the given nesting level.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter.
|
||||
* @param constructor the source constructor (must not be {@code null})
|
||||
@@ -668,11 +687,9 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int, Class)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
|
||||
int parameterIndex) {
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
|
||||
Assert.notNull(constructor, "Constructor must not be null");
|
||||
return forMethodParameter(MethodParameter.forMethodOrConstructor(constructor,
|
||||
parameterIndex));
|
||||
return forMethodParameter(new MethodParameter(constructor, parameterIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,13 +703,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
|
||||
int parameterIndex, Class<?> implementationClass) {
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
|
||||
Assert.notNull(constructor, "Constructor must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
return forMethodParameter(
|
||||
MethodParameter.forMethodOrConstructor(constructor, parameterIndex),
|
||||
implementationClass);
|
||||
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -705,15 +722,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forMethodParameter(MethodParameter.forMethodOrConstructor(method,
|
||||
parameterIndex));
|
||||
return forMethodParameter(new MethodParameter(method, parameterIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a
|
||||
* given implementation. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation
|
||||
* class.
|
||||
* includes generic parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source method (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
@@ -721,74 +736,51 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @see #forMethodParameter(Method, int, Class)
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forMethodParameter(
|
||||
MethodParameter.forMethodOrConstructor(method, parameterIndex),
|
||||
implementationClass);
|
||||
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter}.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(MethodParameter, Class)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
return forType(methodParameter.getGenericParameterType()).getNested(
|
||||
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a
|
||||
* given implementation. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation
|
||||
* class.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter,
|
||||
Class<?> implementationClass) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
methodParameter.getMember().getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (methodParameter.getContainingClass() != null ?
|
||||
forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()) : null);
|
||||
return forType(methodParameter.getGenericParameterType(), variableResolver).getNested(
|
||||
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return.
|
||||
* @param method the source for the method return
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* @param method the source for the method return type
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturn(Method, Class)
|
||||
* @see #forMethodReturnType(Method, Class)
|
||||
*/
|
||||
public static ResolvableType forMethodReturn(Method method) {
|
||||
public static ResolvableType forMethodReturnType(Method method) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forType(method.getGenericReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return. Use this
|
||||
* variant when the class that declares the method includes generic parameter
|
||||
* variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Use this variant when the class that declares the method includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return type
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturn(Method)
|
||||
* @see #forMethodReturnType(Method)
|
||||
*/
|
||||
public static ResolvableType forMethodReturn(Method method,
|
||||
Class<?> implementationClass) {
|
||||
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
method.getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(method.getDeclaringClass()) : null);
|
||||
return forType(method.getGenericReturnType(), variableResolver);
|
||||
}
|
||||
|
||||
@@ -826,12 +818,10 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
private static class WildcardBounds {
|
||||
|
||||
|
||||
private final Kind kind;
|
||||
|
||||
private final ResolvableType[] bounds;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor to create a new {@link WildcardBounds} instance.
|
||||
* @param kind the kind of bounds
|
||||
@@ -843,7 +833,6 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return {@code true} if this bounds is the same kind as the specified bounds.
|
||||
*/
|
||||
@@ -868,18 +857,16 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
|
||||
private boolean isAssignable(ResolvableType source, ResolvableType from) {
|
||||
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from)
|
||||
: from.isAssignableFrom(source));
|
||||
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from) : from.isAssignableFrom(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying bounds.
|
||||
*/
|
||||
public ResolvableType[] getBounds() {
|
||||
return bounds;
|
||||
return this.bounds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a {@link WildcardBounds} instance for the specified type, returning
|
||||
* {@code null} if the specified type cannot be resolved to a {@link WildcardType}.
|
||||
@@ -888,17 +875,15 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static WildcardBounds get(ResolvableType type) {
|
||||
ResolvableType resolveToWildcard = type;
|
||||
while(!(resolveToWildcard.getType() instanceof WildcardType)) {
|
||||
while (!(resolveToWildcard.getType() instanceof WildcardType)) {
|
||||
if (resolveToWildcard == NONE) {
|
||||
return null;
|
||||
}
|
||||
resolveToWildcard = resolveToWildcard.resolveType();
|
||||
}
|
||||
WildcardType wildcardType = (WildcardType) resolveToWildcard.type;
|
||||
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER
|
||||
: Kind.UPPER);
|
||||
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds()
|
||||
: wildcardType.getLowerBounds();
|
||||
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER : Kind.UPPER);
|
||||
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds();
|
||||
ResolvableType[] resolvableBounds = new ResolvableType[bounds.length];
|
||||
for (int i = 0; i < bounds.length; i++) {
|
||||
resolvableBounds[i] = forType(bounds[i], type.variableResolver);
|
||||
@@ -906,11 +891,10 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
return new WildcardBounds(boundsType, resolvableBounds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The various kinds of bounds.
|
||||
*/
|
||||
static enum Kind { UPPER, LOWER }
|
||||
static enum Kind {UPPER, LOWER}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,16 @@ import java.lang.reflect.TypeVariable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0
|
||||
* @see ResolvableType
|
||||
* @see GenericTypeResolver
|
||||
*/
|
||||
public interface TypeVariableResolver {
|
||||
interface TypeVariableResolver {
|
||||
|
||||
/**
|
||||
* Resolve the specified type variable.
|
||||
* @param typeVariable the type variable to resolve (must not be {@code null})
|
||||
* @return the resolved {@link java.lang.reflect.Type} for the variable or
|
||||
* {@code null} if the variable cannot be resolved.
|
||||
* {@code null} if the variable cannot be resolved.
|
||||
*/
|
||||
Type resolveVariable(TypeVariable<?> typeVariable);
|
||||
|
||||
|
||||
@@ -44,13 +44,12 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResolvableType}.
|
||||
@@ -196,7 +195,7 @@ public class ResolvableTypeTests {
|
||||
@Test
|
||||
public void forMethodReturn() throws Exception {
|
||||
Method method = Methods.class.getMethod("charSequenceReturn");
|
||||
ResolvableType type = ResolvableType.forMethodReturn(method);
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(method);
|
||||
assertThat(type.getType(), equalTo(method.getGenericReturnType()));
|
||||
}
|
||||
|
||||
@@ -204,7 +203,7 @@ public class ResolvableTypeTests {
|
||||
public void forMethodReturnMustNotBeNull() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Method must not be null");
|
||||
ResolvableType.forMethodReturn(null);
|
||||
ResolvableType.forMethodReturnType(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -557,25 +556,25 @@ public class ResolvableTypeTests {
|
||||
|
||||
@Test
|
||||
public void resolveBoundedTypeVariableResult() throws Exception {
|
||||
ResolvableType type = ResolvableType.forMethodReturn(Methods.class.getMethod("boundedTypeVaraibleResult"));
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVaraibleResult"));
|
||||
assertThat(type.resolve(), equalTo((Class) CharSequence.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveVariableNotFound() throws Exception {
|
||||
ResolvableType type = ResolvableType.forMethodReturn(Methods.class.getMethod("typedReturn"));
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
|
||||
assertThat(type.resolve(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveTypeVaraibleFromMethodReturn() throws Exception {
|
||||
ResolvableType type = ResolvableType.forMethodReturn(Methods.class.getMethod("typedReturn"));
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
|
||||
assertThat(type.resolve(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveTypeVaraibleFromMethodReturnWithInstanceClass() throws Exception {
|
||||
ResolvableType type = ResolvableType.forMethodReturn(
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(
|
||||
Methods.class.getMethod("typedReturn"), TypedMethods.class);
|
||||
assertThat(type.resolve(), equalTo((Class) String.class));
|
||||
}
|
||||
@@ -685,11 +684,11 @@ public class ResolvableTypeTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveTypeVariableFromMethodParameterTypeWithImplementsClass()
|
||||
throws Exception {
|
||||
public void resolveTypeVariableFromMethodParameterTypeWithImplementsClass() throws Exception {
|
||||
Method method = Methods.class.getMethod("typedParameter", Object.class);
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0);
|
||||
ResolvableType type = ResolvableType.forMethodParameter(methodParameter, TypedMethods.class);
|
||||
methodParameter.setContainingClass(TypedMethods.class);
|
||||
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
|
||||
assertThat(type.resolve(), equalTo((Class) String.class));
|
||||
assertThat(type.getType().toString(), equalTo("T"));
|
||||
}
|
||||
@@ -697,7 +696,7 @@ public class ResolvableTypeTests {
|
||||
@Test
|
||||
public void resolveTypeVariableFromMethodReturn() throws Exception {
|
||||
Method method = Methods.class.getMethod("typedReturn");
|
||||
ResolvableType type = ResolvableType.forMethodReturn(method);
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(method);
|
||||
assertThat(type.resolve(), nullValue());
|
||||
assertThat(type.getType().toString(), equalTo("T"));
|
||||
}
|
||||
@@ -705,7 +704,7 @@ public class ResolvableTypeTests {
|
||||
@Test
|
||||
public void resolveTypeVariableFromMethodReturnWithImplementsClass() throws Exception {
|
||||
Method method = Methods.class.getMethod("typedReturn");
|
||||
ResolvableType type = ResolvableType.forMethodReturn(method, TypedMethods.class);
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(method, TypedMethods.class);
|
||||
assertThat(type.resolve(), equalTo((Class) String.class));
|
||||
assertThat(type.getType().toString(), equalTo("T"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -72,15 +73,6 @@ public class HandlerMethod {
|
||||
this.parameters = initMethodParameters();
|
||||
}
|
||||
|
||||
private MethodParameter[] initMethodParameters() {
|
||||
int count = this.bridgedMethod.getParameterTypes().length;
|
||||
MethodParameter[] result = new MethodParameter[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = new HandlerMethodParameter(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from a bean instance, method name, and parameter types.
|
||||
* @throws NoSuchMethodException when the method cannot be found
|
||||
@@ -138,6 +130,16 @@ public class HandlerMethod {
|
||||
this.parameters = handlerMethod.parameters;
|
||||
}
|
||||
|
||||
|
||||
private MethodParameter[] initMethodParameters() {
|
||||
int count = this.bridgedMethod.getParameterTypes().length;
|
||||
MethodParameter[] result = new MethodParameter[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = new HandlerMethodParameter(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bean for this handler method.
|
||||
*/
|
||||
@@ -157,9 +159,8 @@ public class HandlerMethod {
|
||||
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
|
||||
*/
|
||||
public Class<?> getBeanType() {
|
||||
Class<?> clazz = (this.bean instanceof String)
|
||||
? this.beanFactory.getType((String) this.bean) : this.bean.getClass();
|
||||
|
||||
Class<?> clazz = (this.bean instanceof String ?
|
||||
this.beanFactory.getType((String) this.bean) : this.bean.getClass());
|
||||
return ClassUtils.getUserClass(clazz);
|
||||
}
|
||||
|
||||
@@ -223,12 +224,12 @@ public class HandlerMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (o != null && o instanceof HandlerMethod) {
|
||||
HandlerMethod other = (HandlerMethod) o;
|
||||
if (obj != null && obj instanceof HandlerMethod) {
|
||||
HandlerMethod other = (HandlerMethod) obj;
|
||||
return this.bean.equals(other.bean) && this.method.equals(other.method);
|
||||
}
|
||||
return false;
|
||||
@@ -236,25 +237,26 @@ public class HandlerMethod {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * this.bean.hashCode() + this.method.hashCode();
|
||||
return this.bean.hashCode() * 31 + this.method.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return method.toGenericString();
|
||||
return this.method.toGenericString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A MethodParameter with HandlerMethod-specific behavior.
|
||||
*/
|
||||
private class HandlerMethodParameter extends MethodParameter {
|
||||
|
||||
protected HandlerMethodParameter(int index) {
|
||||
public HandlerMethodParameter(int index) {
|
||||
super(HandlerMethod.this.bridgedMethod, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
public Class<?> getContainingClass() {
|
||||
return HandlerMethod.this.getBeanType();
|
||||
}
|
||||
|
||||
@@ -264,6 +266,7 @@ public class HandlerMethod {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A MethodParameter for a HandlerMethod return type based on an actual return value.
|
||||
*/
|
||||
@@ -278,7 +281,7 @@ public class HandlerMethod {
|
||||
|
||||
@Override
|
||||
public Class<?> getParameterType() {
|
||||
return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType();
|
||||
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -157,9 +158,8 @@ public class HandlerMethod {
|
||||
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
|
||||
*/
|
||||
public Class<?> getBeanType() {
|
||||
Class<?> clazz = (this.bean instanceof String)
|
||||
? this.beanFactory.getType((String) this.bean) : this.bean.getClass();
|
||||
|
||||
Class<?> clazz = (this.bean instanceof String ?
|
||||
this.beanFactory.getType((String) this.bean) : this.bean.getClass());
|
||||
return ClassUtils.getUserClass(clazz);
|
||||
}
|
||||
|
||||
@@ -223,12 +223,12 @@ public class HandlerMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (o != null && o instanceof HandlerMethod) {
|
||||
HandlerMethod other = (HandlerMethod) o;
|
||||
if (obj != null && obj instanceof HandlerMethod) {
|
||||
HandlerMethod other = (HandlerMethod) obj;
|
||||
return this.bean.equals(other.bean) && this.method.equals(other.method);
|
||||
}
|
||||
return false;
|
||||
@@ -236,25 +236,26 @@ public class HandlerMethod {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * this.bean.hashCode() + this.method.hashCode();
|
||||
return this.bean.hashCode() * 31 + this.method.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return method.toGenericString();
|
||||
return this.method.toGenericString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A MethodParameter with HandlerMethod-specific behavior.
|
||||
*/
|
||||
private class HandlerMethodParameter extends MethodParameter {
|
||||
|
||||
protected HandlerMethodParameter(int index) {
|
||||
public HandlerMethodParameter(int index) {
|
||||
super(HandlerMethod.this.bridgedMethod, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
public Class<?> getContainingClass() {
|
||||
return HandlerMethod.this.getBeanType();
|
||||
}
|
||||
|
||||
@@ -264,6 +265,7 @@ public class HandlerMethod {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A MethodParameter for a HandlerMethod return type based on an actual return value.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -167,7 +167,7 @@ public final class ModelFactory {
|
||||
}
|
||||
else {
|
||||
Method method = returnType.getMethod();
|
||||
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getDeclaringClass());
|
||||
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
|
||||
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
@@ -56,12 +56,14 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
|
||||
protected final List<MediaType> allSupportedMediaTypes;
|
||||
|
||||
|
||||
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = messageConverters;
|
||||
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the media types supported by all provided message converters sorted
|
||||
* by specificity via {@link MediaType#sortBySpecificity(List)}.
|
||||
@@ -77,9 +79,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the method argument value of the expected parameter type by
|
||||
* Create the method argument value of the expected parameter type by
|
||||
* reading from the given request.
|
||||
*
|
||||
* @param <T> the expected type of the argument value to be created
|
||||
* @param webRequest the current request
|
||||
* @param methodParam the method argument
|
||||
@@ -96,9 +97,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the method argument value of the expected parameter type by reading
|
||||
* Create the method argument value of the expected parameter type by reading
|
||||
* from the given HttpInputMessage.
|
||||
*
|
||||
* @param <T> the expected type of the argument value to be created
|
||||
* @param inputMessage the HTTP input message representing the current request
|
||||
* @param methodParam the method argument
|
||||
@@ -113,43 +113,42 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
|
||||
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
Class<?> contextClass = methodParam.getDeclaringClass();
|
||||
Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
|
||||
ResolvableType.forMethodParameter(methodParam)).resolve();
|
||||
Class<?> contextClass = methodParam.getContainingClass();
|
||||
Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
|
||||
ResolvableType.forMethodParameter(methodParam)).resolve();
|
||||
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
|
||||
if (genericConverter.canRead(targetType, contextClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetType + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
}
|
||||
return genericConverter.read(targetType, contextClass, inputMessage);
|
||||
}
|
||||
}
|
||||
if (targetClass != null) {
|
||||
if (converter.canRead(targetClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
|
||||
}
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
|
||||
if (genericConverter.canRead(targetType, contextClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetType + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
}
|
||||
return genericConverter.read(targetType, contextClass, inputMessage);
|
||||
}
|
||||
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
|
||||
}
|
||||
if (targetClass != null) {
|
||||
if (converter.canRead(targetClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
|
||||
*
|
||||
* Create a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
|
||||
* @param webRequest the web request to create an input message from
|
||||
* @return the input message
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -80,7 +80,7 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
|
||||
|
||||
if (this.mavResolvers != null) {
|
||||
for (ModelAndViewResolver mavResolver : this.mavResolvers) {
|
||||
Class<?> handlerType = returnType.getDeclaringClass();
|
||||
Class<?> handlerType = returnType.getContainingClass();
|
||||
Method method = returnType.getMethod();
|
||||
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
|
||||
ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, request);
|
||||
@@ -95,14 +95,13 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
|
||||
}
|
||||
}
|
||||
|
||||
// No suitable ModelAndViewResolver..
|
||||
|
||||
// No suitable ModelAndViewResolver...
|
||||
if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
|
||||
this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Unexpected return type: "
|
||||
+ returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,7 +22,6 @@ import java.io.PushbackInputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
@@ -81,16 +80,16 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
|
||||
@Override
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return ((AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), ResponseBody.class) != null)
|
||||
|| (returnType.getMethodAnnotation(ResponseBody.class) != null));
|
||||
return ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) ||
|
||||
(returnType.getMethodAnnotation(ResponseBody.class) != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws MethodArgumentNotValidException if validation fails
|
||||
* @throws HttpMessageNotReadableException if {@link RequestBody#required()}
|
||||
* is {@code true} and there is no body content or if there is no suitable
|
||||
* converter to read the content with.
|
||||
* is {@code true} and there is no body content or if there is no suitable
|
||||
* converter to read the content with.
|
||||
*/
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
|
||||
Reference in New Issue
Block a user