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 3cdee347804..bcef2720cc8 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -243,10 +243,10 @@ public abstract class GenericTypeResolver { */ public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc); - if (!type.hasGenerics() || type.hasUnresolvableGenerics()) { + if (!type.hasGenerics() || type.isEntirelyUnresolvable()) { return null; } - return type.resolveGenerics(); + return type.resolveGenerics(Object.class); } /** 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 6352d2f6751..6a50b919438 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -416,7 +416,24 @@ public final class ResolvableType implements Serializable { } /** - * Determine whether the underlying type has unresolvable generics: + * Return {@code true} if this type contains unresolvable generics only, + * that is, no substitute for any of its declared type variables. + */ + boolean isEntirelyUnresolvable() { + if (this == NONE) { + return false; + } + ResolvableType[] generics = getGenerics(); + for (ResolvableType generic : generics) { + if (!generic.isUnresolvableTypeVariable() && !generic.isWildcardWithoutBounds()) { + return false; + } + } + return true; + } + + /** + * Determine whether the underlying type has any 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. @@ -634,8 +651,8 @@ public final class ResolvableType implements Serializable { /** * Convenience method that will {@link #getGeneric(int...) get} and * {@link #resolve() resolve} a specific generic parameters. - * @param indexes the indexes that refer to the generic parameter (may be omitted to - * return the first generic) + * @param indexes the indexes that refer to the generic parameter + * (may be omitted to return the first generic) * @return a resolved {@link Class} or {@code null} * @see #getGeneric(int...) * @see #resolve() @@ -645,11 +662,11 @@ public final class ResolvableType implements Serializable { } /** - * Resolve this type to a {@link java.lang.Class}, returning {@code null} if the type - * cannot be resolved. This method will consider bounds of {@link TypeVariable}s and - * {@link WildcardType}s if direct resolution fails; however, bounds of - * {@code Object.class} will be ignored. - * @return the resolved {@link Class} or {@code null} + * Resolve this type to a {@link java.lang.Class}, returning {@code null} + * if the type cannot be resolved. This method will consider bounds of + * {@link TypeVariable}s and {@link WildcardType}s if direct resolution fails; + * however, bounds of {@code Object.class} will be ignored. + * @return the resolved {@link Class}, or {@code null} if not resolvable * @see #resolve(Class) * @see #resolveGeneric(int...) * @see #resolveGenerics() diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java index 1f1821bfd4e..94c48448f5b 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -16,6 +16,7 @@ package org.springframework.core; +import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -166,11 +167,21 @@ public class GenericTypeResolverTests { public void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception { // SPR-11044 Class resolved = GenericTypeResolver.resolveReturnType( - WithArrayBase.class.getDeclaredMethod("array", Object[].class), - WithArray.class); + WithArrayBase.class.getDeclaredMethod("array", Object[].class), WithArray.class); assertThat(resolved, equalTo((Class) Object[].class)); } + @Test + public void resolveIncompleteTypeVariables() { + // SPR-11763 + Class[] resolved = GenericTypeResolver.resolveTypeArguments(IdFixingRepository.class, Repository.class); + assertNotNull(resolved); + assertEquals(2, resolved.length); + assertEquals(Object.class, resolved[0]); + assertEquals(Long.class, resolved[1]); + } + + public interface MyInterfaceType { } @@ -314,4 +325,10 @@ public class GenericTypeResolverTests { static abstract class WithArray extends WithArrayBase { } + interface Repository { + } + + interface IdFixingRepository extends Repository { + } + }