diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java index 6cdda4a04b..140dde8bb7 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -343,19 +343,28 @@ private ResolvableType getRepositoryInterface(RepositoryConfiguration configu RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface); List> types = List.of(repositoryInterface, metadata.getDomainType(), metadata.getIdType()); - ResolvableType factoryBeanType = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean); - ResolvableType[] factoryGenerics = factoryBeanType.getGenerics(); + ResolvableType[] declaredGenerics = ResolvableType.forClass(factoryBean).getGenerics(); + ResolvableType[] parentGenerics = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean) + .getGenerics(); + List resolvedGenerics = new ArrayList(factoryBean.getTypeParameters().length); - for (int i = 0; i < factoryGenerics.length; i++) { - ResolvableType parameter = factoryGenerics[i]; + for (int i = 0; i < parentGenerics.length; i++) { - if (parameter.getType() instanceof TypeVariable && i < types.size()) { - factoryGenerics[i] = ResolvableType.forClass(types.get(i)); + ResolvableType parameter = parentGenerics[i]; + + if (parameter.getType() instanceof TypeVariable) { + resolvedGenerics.add(i < types.size() ? ResolvableType.forClass(types.get(i)) : parameter); + } + } + + if (resolvedGenerics.size() < declaredGenerics.length) { + for (int j = parentGenerics.length; j < declaredGenerics.length; j++) { + resolvedGenerics.add(declaredGenerics[j]); } } - return ResolvableType.forClassWithGenerics(factoryBean, factoryGenerics); + return ResolvableType.forClassWithGenerics(factoryBean, resolvedGenerics.toArray(ResolvableType[]::new)); } /** diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java index 5f7907fbc2..71a13568d7 100644 --- a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.*; +import java.lang.reflect.TypeVariable; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -39,6 +40,7 @@ import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.type.AnnotationMetadata; @@ -232,6 +234,38 @@ void registersAotPostProcessorForDifferentConfigurations() { assertThat(context.getBeanNamesForType(RepositoryRegistrationAotProcessor.class)).hasSize(2); } + @Test // GH-3074 + void registersGenericsForIdConstrainingRepositoryFactoryBean() { + + ResolvableType it = registerBeanDefinition(IdConstrainingRepositoryFactoryBean.class); + + assertThat(it.getGenerics()).hasSize(2); + assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class); + assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class); + } + + @Test // GH-3074 + void registersGenericsForDomainTypeConstrainingRepositoryFactoryBean() { + + ResolvableType it = registerBeanDefinition(DomainTypeConstrainingRepositoryFactoryBean.class); + + assertThat(it.getGenerics()).hasSize(2); + assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class); + assertThat(it.getGeneric(1).resolve()).isEqualTo(String.class); + } + + @Test // GH-3074 + void registersGenericsForAdditionalGenericsRepositoryFactoryBean() { + + ResolvableType it = registerBeanDefinition(AdditionalGenericsRepositoryFactoryBean.class); + + assertThat(it.getGenerics()).hasSize(4); + assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class); + assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class); + assertThat(it.getGeneric(2).resolve()).isEqualTo(String.class); + assertThat(it.getGeneric(3).getType()).isInstanceOf(TypeVariable.class); + } + private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class configClass) { var context = new AnnotationConfigApplicationContext(configClass); @@ -289,8 +323,7 @@ protected String getModulePrefix() { } } - @Test // GH-3074 - void registersGenericsForConstrainingRepositoryFactoryBean() { + private ResolvableType registerBeanDefinition(Class repositoryFactoryType) { AnnotationMetadata metadata = AnnotationMetadata.introspect(AnnotatedBeanNamesConfig.class); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -301,7 +334,7 @@ void registersGenericsForConstrainingRepositoryFactoryBean() { @Override public Optional getRepositoryFactoryBeanClassName() { - return Optional.of(IdConstrainingRepositoryFactoryBean.class.getName()); + return Optional.of(repositoryFactoryType.getName()); } }; @@ -313,11 +346,9 @@ public Optional getRepositoryFactoryBeanClassName() { assertThat(repositories).hasSize(1).element(0) .extracting(BeanComponentDefinition::getBeanDefinition) .extracting(BeanDefinition::getResolvableType) - .satisfies(it -> { - assertThat(it.getGenerics()).hasSize(2); - assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class); - assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class); - }); + .isNotNull(); + + return repositories.get(0).getBeanDefinition().getResolvableType(); } static abstract class IdConstrainingRepositoryFactoryBean, S> @@ -327,4 +358,20 @@ protected IdConstrainingRepositoryFactoryBean(Class repositoryInter super(repositoryInterface); } } + + static abstract class DomainTypeConstrainingRepositoryFactoryBean, ID> + extends RepositoryFactoryBeanSupport { + + protected DomainTypeConstrainingRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + } + + static abstract class AdditionalGenericsRepositoryFactoryBean, S, ID, R> + extends RepositoryFactoryBeanSupport { + + protected AdditionalGenericsRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + } }