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 083964732733..c4b83c3707ac 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -193,6 +193,9 @@ else if (genericType instanceof ParameterizedType parameterizedType) { else if (typeArgument instanceof ParameterizedType) { generics[i] = ResolvableType.forType(resolveType(typeArgument, contextClass)); } + else if (typeArgument instanceof WildcardType) { + generics[i] = ResolvableType.forType(resolveType(typeArgument, contextClass)); + } else { generics[i] = ResolvableType.forType(typeArgument); } @@ -203,6 +206,12 @@ else if (typeArgument instanceof ParameterizedType) { } } } + else if (genericType instanceof WildcardType wildcardType) { + Type[] upperBounds = wildcardType.getUpperBounds(); + if (upperBounds.length == 1) { + return resolveType(upperBounds[0], contextClass); + } + } } return genericType; } 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 7e4e3542c021..eba184bf57c9 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -260,6 +260,16 @@ void resolveTypeFromNestedParameterizedType() { assertThat(resolvedType).isEqualTo(method(MyOptionalInterfaceType.class, "get").getGenericReturnType()); } + @Test + void resolveWildcardTypeArgument() { + Type genericReturnType = method(WildcardService.class, "findAll").getGenericReturnType(); + Type resolvedType = resolveType(genericReturnType, PersonWildcardService.class); + assertThat(resolvedType).isInstanceOf(ParameterizedType.class); + ParameterizedType parameterizedType = (ParameterizedType) resolvedType; + assertThat(parameterizedType.getRawType()).isEqualTo(List.class); + assertThat(parameterizedType.getActualTypeArguments()[0]).isEqualTo(String.class); + } + private static Method method(Class target, String methodName, Class... parameterTypes) { Method method = findMethod(target, methodName, parameterTypes); assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull(); @@ -504,4 +514,17 @@ static class ConcreteType implements InterfaceWithDefaultMethod.AbstractType { } } + interface WildcardService { + + List findAll(); + } + + static class PersonWildcardService implements WildcardService { + + @Override + public List findAll() { + return List.of(); + } + } + }