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 8ccad7399596..70d6530b285d 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -159,6 +159,9 @@ public static Type resolveType(Type genericType, @Nullable Class contextClass if (genericType instanceof TypeVariable typeVariable) { ResolvableType resolvedTypeVariable = resolveVariable( typeVariable, ResolvableType.forClass(contextClass)); + if (resolvedTypeVariable == ResolvableType.NONE) { + resolvedTypeVariable = ResolvableType.forVariableBounds(typeVariable); + } if (resolvedTypeVariable != ResolvableType.NONE) { Class resolved = resolvedTypeVariable.resolve(); if (resolved != null) { @@ -176,6 +179,9 @@ else if (genericType instanceof ParameterizedType parameterizedType) { Type typeArgument = typeArguments[i]; if (typeArgument instanceof TypeVariable typeVariable) { ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType); + if (resolvedTypeArgument == ResolvableType.NONE) { + resolvedTypeArgument = ResolvableType.forVariableBounds(typeVariable); + } if (resolvedTypeArgument != ResolvableType.NONE) { generics[i] = resolvedTypeArgument; } @@ -229,7 +235,7 @@ private static ResolvableType resolveVariable(TypeVariable typeVariable, Reso return resolvedType; } } - return ResolvableType.forVariableBounds(typeVariable); + return ResolvableType.NONE; } /** 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 6dd84766acbf..df63c65e2455 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -243,6 +243,13 @@ void resolveTypeWithUnresolvableElement() { assertThat(resolvedType.toString()).isEqualTo("java.util.List"); } + @Test + void resolveTypeFromGenericDefaultMethod() { + Type type = method(InterfaceWithDefaultMethod.class, "get", InheritsDefaultMethod.AbstractType.class).getGenericParameterTypes()[0]; + Type resolvedType = resolveType(type, InheritsDefaultMethod.class); + assertThat(resolvedType).isEqualTo(InheritsDefaultMethod.ConcreteType.class); + } + private static Method method(Class target, String methodName, Class... parameterTypes) { Method method = findMethod(target, methodName, parameterTypes); assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull(); @@ -454,4 +461,20 @@ List get() { } } + public interface InterfaceWithDefaultMethod { + + default String get(T input) { + throw new UnsupportedOperationException(); + } + + interface AbstractType { + } + } + + public static class InheritsDefaultMethod implements InterfaceWithDefaultMethod { + + static class ConcreteType implements AbstractType { + } + } + }