diff --git a/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/src/main/java/org/springframework/data/util/TypeDiscoverer.java index 3117c9af4..1ee6e3130 100644 --- a/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -331,6 +331,12 @@ class TypeDiscoverer implements TypeInformation { return false; } + // in case types cannot be resolved resort to toString checking to avoid infinite recursion caused by raw types and + // self-referencing generics + if (that.resolvableType.hasUnresolvableGenerics() || this.resolvableType.hasUnresolvableGenerics()) { + return ObjectUtils.nullSafeEquals(that.resolvableType.toString(), this.resolvableType.toString()); + } + return ObjectUtils.nullSafeEquals(resolvedGenerics.get(), that.resolvedGenerics.get()); } diff --git a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java index 8d08c9e0e..c77055ee4 100755 --- a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java +++ b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.util; import static org.assertj.core.api.Assertions.*; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; @@ -33,6 +34,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.core.ResolvableType; import org.springframework.data.geo.GeoResults; +import org.springframework.data.repository.Repository; import org.springframework.util.ReflectionUtils; /** @@ -358,6 +360,24 @@ public class TypeDiscovererUnitTests { assertThat(TypeInformation.of(containerList)).isNotEqualTo(TypeInformation.of(containerMap)); } + @Test // GH-3084 + void considersNestedGenericsInEqualityForRecursiveUnresolvableTypes() throws Exception { + + TypeInformation repro = TypeInformation.of(RepoWithRawGenerics.class); + + Method findAllExternalIdsFor = RepoWithRawGenerics.class.getDeclaredMethod("findAllExternalIdsFor"); + TypeInformation returnType = repro.getReturnType(findAllExternalIdsFor); + + List> arguments = TypeInformation.of(RepoWithRawGenerics.class)// + .getRequiredSuperTypeInformation(Repository.class)// + .getTypeArguments(); + + TypeInformation domainType = arguments.get(1); + TypeInformation actualType = returnType.getRequiredComponentType(); + + assertThat(domainType.isAssignableFrom(actualType)).isTrue(); + } + class Person { Addresses addresses; @@ -462,4 +482,14 @@ public class TypeDiscovererUnitTests { T data; } + static abstract class SomeType> { + + } + + @SuppressWarnings("rawtypes") + interface RepoWithRawGenerics extends Repository { + + > List> findAllExternalIdsFor(); + } + }