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 469e934f1314..e99cf3b228e6 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -872,6 +872,12 @@ private ResolvableType resolveVariable(TypeVariable variable) { return forType(ownerType, this.variableResolver).resolveVariable(variable); } } + if (this.type instanceof WildcardType) { + ResolvableType resolved = resolveType().resolveVariable(variable); + if (resolved != null) { + return resolved; + } + } if (this.variableResolver != null) { return this.variableResolver.resolveVariable(variable); } @@ -997,7 +1003,7 @@ public static ResolvableType forClass(@Nullable Class clazz) { * {@link Class#isAssignableFrom}, which this serves as a wrapper for. * For example: {@code ResolvableType.forRawClass(List.class)}. * @param clazz the class to introspect ({@code null} is semantically - * equivalent to {@code Object.class} for typical use cases here} + * equivalent to {@code Object.class} for typical use cases here) * @return a {@link ResolvableType} for the specified class * @since 4.2 * @see #forClass(Class) @@ -1485,10 +1491,10 @@ public TypeVariablesVariableResolver(TypeVariable[] variables, ResolvableType @Override @Nullable public ResolvableType resolveVariable(TypeVariable variable) { + TypeVariable variableToCompare = SerializableTypeWrapper.unwrap(variable); for (int i = 0; i < this.variables.length; i++) { - TypeVariable v1 = SerializableTypeWrapper.unwrap(this.variables[i]); - TypeVariable v2 = SerializableTypeWrapper.unwrap(variable); - if (ObjectUtils.nullSafeEquals(v1, v2)) { + TypeVariable resolvedVariable = SerializableTypeWrapper.unwrap(this.variables[i]); + if (ObjectUtils.nullSafeEquals(resolvedVariable, variableToCompare)) { return this.generics[i]; } } 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 58421740df09..330f23a6cfff 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -684,38 +684,31 @@ public void doesResolveFromOuterOwner() throws Exception { @Test public void resolveBoundedTypeVariableResult() throws Exception { - ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVaraibleResult")); + ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableResult")); assertThat(type.resolve(), equalTo((Class) CharSequence.class)); } @Test - public void resolveVariableNotFound() throws Exception { - ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn")); - assertThat(type.resolve(), nullValue()); + public void resolveBoundedTypeVariableWildcardResult() throws Exception { + ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableWildcardResult")); + assertThat(type.getGeneric(1).asCollection().resolveGeneric(), equalTo((Class) CharSequence.class)); } @Test - public void resolveTypeVaraibleFromMethodReturn() throws Exception { + public void resolveVariableNotFound() throws Exception { ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn")); assertThat(type.resolve(), nullValue()); } @Test - public void resolveTypeVaraibleFromMethodReturnWithInstanceClass() throws Exception { - ResolvableType type = ResolvableType.forMethodReturnType( - Methods.class.getMethod("typedReturn"), TypedMethods.class); - assertThat(type.resolve(), equalTo((Class) String.class)); - } - - @Test - public void resolveTypeVaraibleFromSimpleInterfaceType() { + public void resolveTypeVariableFromSimpleInterfaceType() { ResolvableType type = ResolvableType.forClass( MySimpleInterfaceType.class).as(MyInterfaceType.class); assertThat(type.resolveGeneric(), equalTo((Class) String.class)); } @Test - public void resolveTypeVaraibleFromSimpleCollectionInterfaceType() { + public void resolveTypeVariableFromSimpleCollectionInterfaceType() { ResolvableType type = ResolvableType.forClass( MyCollectionInterfaceType.class).as(MyInterfaceType.class); assertThat(type.resolveGeneric(), equalTo((Class) Collection.class)); @@ -723,14 +716,14 @@ public void resolveTypeVaraibleFromSimpleCollectionInterfaceType() { } @Test - public void resolveTypeVaraibleFromSimpleSuperclassType() { + public void resolveTypeVariableFromSimpleSuperclassType() { ResolvableType type = ResolvableType.forClass( MySimpleSuperclassType.class).as(MySuperclassType.class); assertThat(type.resolveGeneric(), equalTo((Class) String.class)); } @Test - public void resolveTypeVaraibleFromSimpleCollectionSuperclassType() { + public void resolveTypeVariableFromSimpleCollectionSuperclassType() { ResolvableType type = ResolvableType.forClass( MyCollectionSuperclassType.class).as(MySuperclassType.class); assertThat(type.resolveGeneric(), equalTo((Class) Collection.class)); @@ -1459,7 +1452,9 @@ interface Methods { void charSequenceParameter(List cs); - R boundedTypeVaraibleResult(); + R boundedTypeVariableResult(); + + Map> boundedTypeVariableWildcardResult(); void nested(Map, Map> p);