diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
index c557abe5060..eeea2e09c66 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
@@ -212,6 +212,29 @@ public class DependencyDescriptor implements Serializable {
ResolvableType.forMethodParameter(this.methodParameter));
}
+ /**
+ * Return whether a fallback match is allowed.
+ *
This is {@code false} by default but may be overridden to return {@code true} in order
+ * to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
+ * that a fallback match is acceptable as well.
+ */
+ public boolean fallbackMatchAllowed() {
+ return false;
+ }
+
+ /**
+ * Return a variant of this descriptor that is intended for a fallback match.
+ * @see #fallbackMatchAllowed()
+ */
+ public DependencyDescriptor forFallbackMatch() {
+ return new DependencyDescriptor(this) {
+ @Override
+ public boolean fallbackMatchAllowed() {
+ return true;
+ }
+ };
+ }
+
/**
* Initialize parameter name discovery for the underlying method parameter, if any.
*
This method does not actually try to retrieve the parameter name at
@@ -241,7 +264,8 @@ public class DependencyDescriptor implements Serializable {
if (this.nestingLevel > 1) {
Type type = this.field.getGenericType();
if (type instanceof ParameterizedType) {
- Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
+ Type[] args = ((ParameterizedType) type).getActualTypeArguments();
+ Type arg = args[args.length - 1];
if (arg instanceof Class) {
return (Class) arg;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 4cff2d0e641..8c972e4fc9c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -979,6 +979,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
result.put(candidateName, getBean(candidateName));
}
}
+ if (result.isEmpty()) {
+ DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
+ for (String candidateName : candidateNames) {
+ if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
+ result.put(candidateName, getBean(candidateName));
+ }
+ }
+ }
return result;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
index 256c981169a..08df71f59ce 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
@@ -57,24 +57,28 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
// if explicitly false, do not proceed with any other checks
return false;
}
- return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor.getResolvableType()));
+ return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
}
/**
* Match the given dependency type with its generic type information
* against the given candidate bean definition.
*/
- protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, ResolvableType dependencyType) {
- if (dependencyType.getType() instanceof Class) {
+ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
+ ResolvableType dependencyType = descriptor.getResolvableType();
+ if (!dependencyType.hasGenerics()) {
// No generic type -> we know it's a Class type-match, so no need to check again.
return true;
}
- RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
ResolvableType targetType = null;
- if (bd.getResolvedFactoryMethod() != null) {
+ RootBeanDefinition rbd = null;
+ if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
+ rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
+ }
+ if (rbd != null && rbd.getResolvedFactoryMethod() != null) {
// Should typically be set for any kind of factory method, since the BeanFactory
// pre-resolves them before reaching out to the AutowireCandidateResolver...
- targetType = ResolvableType.forMethodReturnType(bd.getResolvedFactoryMethod());
+ targetType = ResolvableType.forMethodReturnType(rbd.getResolvedFactoryMethod());
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
@@ -86,14 +90,20 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
}
// Fallback: no BeanFactory set, or no type resolvable through it
// -> best-effort match against the target class if applicable.
- if (targetType == null && bd.hasBeanClass() && bd.getFactoryMethodName() == null) {
- Class> beanClass = bd.getBeanClass();
+ if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
+ Class> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
}
- return (targetType == null || dependencyType.isAssignableFrom(targetType));
+ if (targetType == null) {
+ return true;
+ }
+ if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
+ return descriptor.getDependencyType().isAssignableFrom(targetType.getRawClass());
+ }
+ return dependencyType.isAssignableFrom(targetType);
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
index d6c8fce2bbb..e53b7661422 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
@@ -1305,6 +1305,130 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
}
+ @Test
+ public void testGenericsBasedConstructorInjectionWithNonTypedTarget() {
+ 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);
+ GenericRepository gr = new GenericRepository();
+ bf.registerSingleton("genericRepo", gr);
+
+ RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertSame(gr, bean.stringRepository);
+ assertSame(gr, bean.integerRepository);
+ assertSame(1, bean.stringRepositoryArray.length);
+ assertSame(1, bean.integerRepositoryArray.length);
+ assertSame(gr, bean.stringRepositoryArray[0]);
+ assertSame(gr, bean.integerRepositoryArray[0]);
+ assertSame(1, bean.stringRepositoryList.size());
+ assertSame(1, bean.integerRepositoryList.size());
+ assertSame(gr, bean.stringRepositoryList.get(0));
+ assertSame(gr, bean.integerRepositoryList.get(0));
+ assertSame(1, bean.stringRepositoryMap.size());
+ assertSame(1, bean.integerRepositoryMap.size());
+ assertSame(gr, bean.stringRepositoryMap.get("genericRepo"));
+ assertSame(gr, bean.integerRepositoryMap.get("genericRepo"));
+ }
+
+ @Test
+ public void testGenericsBasedConstructorInjectionWithNonGenericTarget() {
+ 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);
+ SimpleRepository ngr = new SimpleRepository();
+ bf.registerSingleton("simpleRepo", ngr);
+
+ RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertSame(ngr, bean.stringRepository);
+ assertSame(ngr, bean.integerRepository);
+ assertSame(1, bean.stringRepositoryArray.length);
+ assertSame(1, bean.integerRepositoryArray.length);
+ assertSame(ngr, bean.stringRepositoryArray[0]);
+ assertSame(ngr, bean.integerRepositoryArray[0]);
+ assertSame(1, bean.stringRepositoryList.size());
+ assertSame(1, bean.integerRepositoryList.size());
+ assertSame(ngr, bean.stringRepositoryList.get(0));
+ assertSame(ngr, bean.integerRepositoryList.get(0));
+ assertSame(1, bean.stringRepositoryMap.size());
+ assertSame(1, bean.integerRepositoryMap.size());
+ assertSame(ngr, bean.stringRepositoryMap.get("simpleRepo"));
+ assertSame(ngr, bean.integerRepositoryMap.get("simpleRepo"));
+ }
+
+ @Test
+ public void testGenericsBasedConstructorInjectionWithMixedTargets() {
+ 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);
+ GenericRepository gr = new GenericRepositorySubclass();
+ bf.registerSingleton("genericRepo", gr);
+
+ RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertSame(sr, bean.stringRepository);
+ assertSame(gr, bean.integerRepository);
+ assertSame(1, bean.stringRepositoryArray.length);
+ assertSame(1, bean.integerRepositoryArray.length);
+ assertSame(sr, bean.stringRepositoryArray[0]);
+ assertSame(gr, bean.integerRepositoryArray[0]);
+ assertSame(1, bean.stringRepositoryList.size());
+ assertSame(1, bean.integerRepositoryList.size());
+ assertSame(sr, bean.stringRepositoryList.get(0));
+ assertSame(gr, bean.integerRepositoryList.get(0));
+ assertSame(1, bean.stringRepositoryMap.size());
+ assertSame(1, bean.integerRepositoryMap.size());
+ assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
+ assertSame(gr, bean.integerRepositoryMap.get("genericRepo"));
+ }
+
+ @Test
+ public void testGenericsBasedConstructorInjectionWithMixedTargetsIncludingNonGeneric() {
+ 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);
+ SimpleRepository ngr = new SimpleRepositorySubclass();
+ bf.registerSingleton("simpleRepo", ngr);
+
+ RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertSame(sr, bean.stringRepository);
+ assertSame(ngr, bean.integerRepository);
+ assertSame(1, bean.stringRepositoryArray.length);
+ assertSame(1, bean.integerRepositoryArray.length);
+ assertSame(sr, bean.stringRepositoryArray[0]);
+ assertSame(ngr, bean.integerRepositoryArray[0]);
+ assertSame(1, bean.stringRepositoryList.size());
+ assertSame(1, bean.integerRepositoryList.size());
+ assertSame(sr, bean.stringRepositoryList.get(0));
+ assertSame(ngr, bean.integerRepositoryList.get(0));
+ assertSame(1, bean.stringRepositoryMap.size());
+ assertSame(1, bean.integerRepositoryMap.size());
+ assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
+ assertSame(ngr, bean.integerRepositoryMap.get("simpleRepo"));
+ }
+
public static class ResourceInjectionBean {
@@ -1859,6 +1983,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class IntegerRepository implements Repository {
}
+ public static class GenericRepository implements Repository {
+ }
+
+ public static class GenericRepositorySubclass extends GenericRepository {
+ }
+
+ public static class SimpleRepository implements Repository {
+ }
+
+ public static class SimpleRepositorySubclass extends SimpleRepository {
+ }
+
public static class RepositoryFieldInjectionBean {
diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
index 5bcfe63c9bc..a6589b17160 100644
--- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
@@ -251,27 +251,13 @@ public abstract class GenericTypeResolver {
/**
* Resolve the specified generic type against the given TypeVariable map.
* @param genericType the generic type to resolve
- * @param typeVariableMap the TypeVariable Map to resolved against
+ * @param map the TypeVariable Map to resolved against
* @return the type if it resolves to a Class, or {@code Object.class} otherwise
* @deprecated as of Spring 4.0 in favor of {@link ResolvableType}
*/
@Deprecated
- public static Class> resolveType(Type genericType, final Map typeVariableMap) {
-
- ResolvableType.VariableResolver variableResolver = new ResolvableType.VariableResolver() {
- @Override
- public ResolvableType resolveVariable(TypeVariable> variable) {
- Type type = typeVariableMap.get(variable);
- return (type == null ? null : ResolvableType.forType(type));
- }
-
- @Override
- public Object getSource() {
- return typeVariableMap;
- }
- };
-
- return ResolvableType.forType(genericType, variableResolver).resolve(Object.class);
+ public static Class> resolveType(Type genericType, Map map) {
+ return ResolvableType.forType(genericType, new TypeVariableMapVariableResolver(map)).resolve(Object.class);
}
/**
@@ -315,4 +301,26 @@ public abstract class GenericTypeResolver {
}
}
+
+ @SuppressWarnings("serial")
+ private static class TypeVariableMapVariableResolver implements ResolvableType.VariableResolver {
+
+ private final Map typeVariableMap;
+
+ public TypeVariableMapVariableResolver(Map typeVariableMap) {
+ this.typeVariableMap = typeVariableMap;
+ }
+
+ @Override
+ public ResolvableType resolveVariable(TypeVariable> variable) {
+ Type type = this.typeVariableMap.get(variable);
+ return (type != null ? ResolvableType.forType(type) : null);
+ }
+
+ @Override
+ public Object getSource() {
+ return this.typeVariableMap;
+ }
+ }
+
}
diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
index abd07c90f36..61c06468dcb 100644
--- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
@@ -265,7 +265,8 @@ public class MethodParameter {
Type type = getGenericParameterType();
if (type instanceof ParameterizedType) {
Integer index = getTypeIndexForCurrentLevel();
- Type arg = ((ParameterizedType) type).getActualTypeArguments()[index != null ? index : 0];
+ Type[] args = ((ParameterizedType) type).getActualTypeArguments();
+ Type arg = args[index != null ? index : args.length - 1];
if (arg instanceof Class) {
return (Class) arg;
}
diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java
index acdc30e810a..0ae3f9bd585 100644
--- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java
+++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java
@@ -105,8 +105,7 @@ public final class ResolvableType implements Serializable {
private boolean isResolved = false;
/**
- * Late binding stored copy of the resolved value (valid when {@link #isResolved} is
- * true).
+ * Late binding stored copy of the resolved value (valid when {@link #isResolved} is true).
*/
private Class> resolved;
@@ -116,7 +115,6 @@ public final class ResolvableType implements Serializable {
private final ResolvableType componentType;
-
/**
* Private constructor used to create a new {@link ResolvableType}.
* @param type the underlying java type (may only be {@code null} for {@link #NONE})
@@ -131,13 +129,25 @@ public final class ResolvableType implements Serializable {
/**
- * Return the underling java {@link Type} being managed. With the exception of
+ * Return the underling Java {@link Type} being managed. With the exception of
* the {@link #NONE} constant, this method will never return {@code null}.
*/
public Type getType() {
return this.type;
}
+ /**
+ * Return the underlying Java {@link Class} being managed, if available;
+ * otherwise {@code null}.
+ */
+ public Class> getRawClass() {
+ Type rawType = this.type;
+ if (rawType instanceof ParameterizedType) {
+ rawType = ((ParameterizedType) rawType).getRawType();
+ }
+ return (rawType instanceof Class ? (Class) rawType : null);
+ }
+
/**
* Determines if this {@code ResolvableType} is assignable from the specified
* {@code type}. Attempts to follow the same rules as the Java compiler, considering
@@ -161,8 +171,7 @@ public final class ResolvableType implements Serializable {
// Deal with array by delegating to the component type
if (isArray()) {
- return (type.isArray() && getComponentType().isAssignableFrom(
- type.getComponentType()));
+ return (type.isArray() && getComponentType().isAssignableFrom(type.getComponentType()));
}
// Deal with wildcard bounds
@@ -171,8 +180,8 @@ public final class ResolvableType implements Serializable {
// in the from X is assignable to extends Number>
if (typeBounds != null) {
- return (ourBounds != null && ourBounds.isSameKind(typeBounds)
- && ourBounds.isAssignableFrom(typeBounds.getBounds()));
+ return (ourBounds != null && ourBounds.isSameKind(typeBounds) &&
+ ourBounds.isAssignableFrom(typeBounds.getBounds()));
}
// in the form extends Number> is assignable to X ...
@@ -189,8 +198,7 @@ public final class ResolvableType implements Serializable {
// Recursively check each generic
for (int i = 0; i < getGenerics().length; i++) {
- rtn &= getGeneric(i).isAssignableFrom(
- type.as(resolve(Object.class)).getGeneric(i), true);
+ rtn &= getGeneric(i).isAssignableFrom(type.as(resolve(Object.class)).getGeneric(i), true);
}
return rtn;
@@ -228,8 +236,7 @@ public final class ResolvableType implements Serializable {
return forType(componentType, this.variableResolver);
}
if (this.type instanceof GenericArrayType) {
- return forType(((GenericArrayType) this.type).getGenericComponentType(),
- this.variableResolver);
+ return forType(((GenericArrayType) this.type).getGenericComponentType(), this.variableResolver);
}
return resolveType().getComponentType();
}
@@ -291,7 +298,7 @@ public final class ResolvableType implements Serializable {
* @see #getInterfaces()
*/
public ResolvableType getSuperType() {
- final Class> resolved = resolve();
+ Class> resolved = resolve();
if (resolved == null || resolved.getGenericSuperclass() == null) {
return NONE;
}
@@ -305,7 +312,7 @@ public final class ResolvableType implements Serializable {
* @see #getSuperType()
*/
public ResolvableType[] getInterfaces() {
- final Class> resolved = resolve();
+ Class> resolved = resolve();
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
return EMPTY_TYPES_ARRAY;
}
@@ -321,6 +328,35 @@ public final class ResolvableType implements Serializable {
return (getGenerics().length > 0);
}
+ /**
+ * Determine whether the underlying type has unresolvable generics:
+ * either through an unresolvable type variable on the type itself
+ * or through implementing a generic interface in a raw fashion,
+ * i.e. without substituting that interface's type variables.
+ * The result will be {@code true} only in those two scenarios.
+ */
+ public boolean hasUnresolvableGenerics() {
+ ResolvableType[] generics = getGenerics();
+ for (ResolvableType generic : generics) {
+ if (generic.resolve() == null) {
+ return true;
+ }
+ }
+ Class> resolved = resolve();
+ Type[] ifcs = resolved.getGenericInterfaces();
+ for (Type ifc : ifcs) {
+ if (ifc instanceof Class) {
+ if (forClass((Class) ifc).hasGenerics()) {
+ return true;
+ }
+ }
+ }
+ if (resolved.getGenericSuperclass() != null) {
+ return getSuperType().hasUnresolvableGenerics();
+ }
+ return false;
+ }
+
/**
* Return a {@link ResolvableType} for the specified nesting level. See
* {@link #getNested(int, Map)} for details.
@@ -362,8 +398,7 @@ public final class ResolvableType implements Serializable {
while (result != ResolvableType.NONE && !result.hasGenerics()) {
result = result.getSuperType();
}
- Integer index = (typeIndexesPerLevel == null ? null
- : typeIndexesPerLevel.get(i));
+ Integer index = (typeIndexesPerLevel != null ? typeIndexesPerLevel.get(i) : null);
index = (index == null ? result.getGenerics().length - 1 : index);
result = result.getGeneric(index);
}
@@ -515,10 +550,8 @@ public final class ResolvableType implements Serializable {
* it cannot be serialized.
*/
ResolvableType resolveType() {
-
if (this.type instanceof ParameterizedType) {
- return forType(((ParameterizedType) this.type).getRawType(),
- this.variableResolver);
+ return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver);
}
if (this.type instanceof WildcardType) {
@@ -535,7 +568,7 @@ public final class ResolvableType implements Serializable {
// Try default variable resolution
if (this.variableResolver != null) {
ResolvableType resolved = this.variableResolver.resolveVariable(variable);
- if(resolved != null) {
+ if (resolved != null) {
return resolved;
}
}
@@ -555,7 +588,6 @@ public final class ResolvableType implements Serializable {
}
private ResolvableType resolveVariable(TypeVariable> variable) {
-
if (this.type instanceof TypeVariable) {
return resolveType().resolveVariable(variable);
}
@@ -571,8 +603,7 @@ public final class ResolvableType implements Serializable {
}
if (parameterizedType.getOwnerType() != null) {
- return forType(parameterizedType.getOwnerType(),
- this.variableResolver).resolveVariable(variable);
+ return forType(parameterizedType.getOwnerType(), this.variableResolver).resolveVariable(variable);
}
}
@@ -627,7 +658,7 @@ public final class ResolvableType implements Serializable {
}
/**
- * Custom serialization support for {@value #NONE}.
+ * Custom serialization support for {@link #NONE}.
*/
private Object readResolve() throws ObjectStreamException {
return (this.type == null ? NONE : this);
@@ -640,22 +671,7 @@ public final class ResolvableType implements Serializable {
if (this == NONE) {
return null;
}
-
- return new VariableResolver() {
-
- private static final long serialVersionUID = 1L;
-
-
- @Override
- public ResolvableType resolveVariable(TypeVariable> variable) {
- return ResolvableType.this.resolveVariable(variable);
- }
-
- @Override
- public Object getSource() {
- return ResolvableType.this;
- }
- };
+ return new DefaultVariableResolver();
}
private static boolean variableResolverSourceEquals(VariableResolver o1, VariableResolver o2) {
@@ -842,8 +858,7 @@ public final class ResolvableType implements Serializable {
* @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");
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
methodParameter.setContainingClass(implementationClass);
@@ -858,8 +873,7 @@ public final class ResolvableType implements Serializable {
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
- ResolvableType owner = forType(methodParameter.getContainingClass()).as(
- methodParameter.getDeclaringClass());
+ ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass());
return forType(SerializableTypeWrapper.forMethodParameter(methodParameter),
owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(),
methodParameter.typeIndexesPerLevel);
@@ -877,15 +891,13 @@ public final class ResolvableType implements Serializable {
}
/**
- * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared
- * generics.
+ * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...)
*/
- public static ResolvableType forClassWithGenerics(Class> sourceClass,
- Class>... generics) {
+ public static ResolvableType forClassWithGenerics(Class> sourceClass, Class>... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
ResolvableType[] resolvableGenerics = new ResolvableType[generics.length];
@@ -896,44 +908,17 @@ public final class ResolvableType implements Serializable {
}
/**
- * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared
- * generics.
+ * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param sourceClass the source class
* @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...)
*/
- public static ResolvableType forClassWithGenerics(Class> sourceClass,
- final ResolvableType... generics) {
+ public static ResolvableType forClassWithGenerics(Class> sourceClass, ResolvableType... generics) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null");
- final TypeVariable>[] typeVariables = sourceClass.getTypeParameters();
- Assert.isTrue(typeVariables.length == generics.length,
- "Missmatched number of generics specified");
-
-
- VariableResolver variableResolver = new VariableResolver() {
-
- private static final long serialVersionUID = 1L;
-
-
- @Override
- public ResolvableType resolveVariable(TypeVariable> variable) {
- for (int i = 0; i < typeVariables.length; i++) {
- if(typeVariables[i].equals(variable)) {
- return generics[i];
- }
- }
- return null;
- }
-
- @Override
- public Object getSource() {
- return generics;
- }
- };
-
- return forType(sourceClass, variableResolver);
+ TypeVariable>[] typeVariables = sourceClass.getTypeParameters();
+ return forType(sourceClass, new TypeVariablesVariableResolver(typeVariables, generics));
}
/**
@@ -948,9 +933,8 @@ public final class ResolvableType implements Serializable {
}
/**
- * Return a {@link ResolvableType} for the specified {@link Type} backed by the
- * given owner type. NOTE: The resulting {@link ResolvableType} may not be
- * {@link Serializable}.
+ * Return a {@link ResolvableType} for the specified {@link Type} backed by the given
+ * owner type. NOTE: The resulting {@link ResolvableType} may not be {@link Serializable}.
* @param type the source type or {@code null}
* @param owner the owner type used to resolve variables
* @return a {@link ResolvableType} for the specified {@link Type} and owner
@@ -972,7 +956,7 @@ public final class ResolvableType implements Serializable {
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/
static ResolvableType forType(Type type, VariableResolver variableResolver) {
- if(type == null) {
+ if (type == null) {
return NONE;
}
// Check the cache, we may have a ResolvableType that may have already been resolved
@@ -1002,7 +986,51 @@ public final class ResolvableType implements Serializable {
* @return the resolved variable or {@code null}
*/
ResolvableType resolveVariable(TypeVariable> variable);
+ }
+
+ @SuppressWarnings("serial")
+ private class DefaultVariableResolver implements VariableResolver {
+
+ @Override
+ public ResolvableType resolveVariable(TypeVariable> variable) {
+ return ResolvableType.this.resolveVariable(variable);
+ }
+
+ @Override
+ public Object getSource() {
+ return ResolvableType.this;
+ }
+ }
+
+
+ @SuppressWarnings("serial")
+ private static class TypeVariablesVariableResolver implements VariableResolver {
+
+ private final TypeVariable[] typeVariables;
+
+ private final ResolvableType[] generics;
+
+ public TypeVariablesVariableResolver(TypeVariable[] typeVariables, ResolvableType[] generics) {
+ Assert.isTrue(typeVariables.length == generics.length, "Mismatched number of generics specified");
+ this.typeVariables = typeVariables;
+ this.generics = generics;
+ }
+
+ @Override
+ public ResolvableType resolveVariable(TypeVariable> variable) {
+ for (int i = 0; i < this.typeVariables.length; i++) {
+ if (this.typeVariables[i].equals(variable)) {
+ return this.generics[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Object getSource() {
+ return this.generics;
+ }
}
@@ -1016,12 +1044,12 @@ public final class ResolvableType implements Serializable {
private final ResolvableType[] bounds;
/**
- * Private constructor to create a new {@link WildcardBounds} instance.
+ * Internal constructor to create a new {@link WildcardBounds} instance.
* @param kind the kind of bounds
* @param bounds the bounds
* @see #get(ResolvableType)
*/
- private WildcardBounds(Kind kind, ResolvableType[] bounds) {
+ public WildcardBounds(Kind kind, ResolvableType[] bounds) {
this.kind = kind;
this.bounds = bounds;
}
@@ -1079,8 +1107,7 @@ public final class ResolvableType implements Serializable {
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] = ResolvableType.forType(bounds[i],
- type.variableResolver);
+ resolvableBounds[i] = ResolvableType.forType(bounds[i], type.variableResolver);
}
return new WildcardBounds(boundsType, resolvableBounds);
}
@@ -1089,7 +1116,6 @@ public final class ResolvableType implements Serializable {
* The various kinds of bounds.
*/
static enum Kind {UPPER, LOWER}
-
}
}
diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
index 60cbf3548e2..ab595a7c864 100644
--- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
+++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
@@ -48,24 +48,21 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.runners.MockitoJUnitRunner;
+
import org.springframework.core.ResolvableType.VariableResolver;
import org.springframework.util.MultiValueMap;
-import static org.mockito.BDDMockito.*;
-import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import static org.mockito.BDDMockito.*;
/**
- * Tests for {@link ResolvableType}.
- *
* @author Phillip Webb
*/
@SuppressWarnings("rawtypes")
@RunWith(MockitoJUnitRunner.class)
public class ResolvableTypeTests {
-
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -1070,7 +1067,7 @@ public class ResolvableTypeTests {
@Test
public void forClassWithMismatchedGenerics() throws Exception {
thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("Missmatched number of generics specified");
+ thrown.expectMessage("Mismatched number of generics specified");
ResolvableType.forClassWithGenerics(Map.class, Integer.class);
}