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 1e5eb286c..6cdda4a04 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -49,6 +49,7 @@ import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.metrics.StartupStep; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.AbstractRepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.util.ReflectionUtils; import org.springframework.lang.Nullable; @@ -339,22 +340,22 @@ public class RepositoryConfigurationDelegate { return null; } - TypeVariable[] variables = factoryBean.getTypeParameters(); - int numberOfGenerics = variables.length; RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface); + List> types = List.of(repositoryInterface, metadata.getDomainType(), metadata.getIdType()); - ResolvableType[] generics = new ResolvableType[numberOfGenerics]; - generics[0] = ResolvableType.forClass(repositoryInterface); - generics[1] = ResolvableType.forClass(metadata.getDomainType()); - generics[2] = ResolvableType.forClass(metadata.getIdType()); + ResolvableType factoryBeanType = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean); + ResolvableType[] factoryGenerics = factoryBeanType.getGenerics(); - if (numberOfGenerics > 3) { - for (int i = 3; i < numberOfGenerics; i++) { - generics[i] = ResolvableType.forType(variables[0]); + for (int i = 0; i < factoryGenerics.length; i++) { + + ResolvableType parameter = factoryGenerics[i]; + + if (parameter.getType() instanceof TypeVariable && i < types.size()) { + factoryGenerics[i] = ResolvableType.forClass(types.get(i)); } } - return ResolvableType.forClassWithGenerics(factoryBean, generics); + return ResolvableType.forClassWithGenerics(factoryBean, factoryGenerics); } /** 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 55badb416..5f7907fbc 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,10 @@ package org.springframework.data.repository.config; import static org.assertj.core.api.Assertions.*; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; @@ -26,6 +30,8 @@ import org.mockito.quality.Strictness; import org.springframework.aop.framework.Advised; import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -37,6 +43,8 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.data.mapping.Person; +import org.springframework.data.repository.Repository; import org.springframework.data.repository.config.RepositoryConfigurationDelegate.LazyRepositoryInjectionPointResolver; import org.springframework.data.repository.config.annotated.MyAnnotatedRepository; import org.springframework.data.repository.config.annotated.MyAnnotatedRepositoryImpl; @@ -44,6 +52,7 @@ import org.springframework.data.repository.config.annotated.MyFragmentImpl; import org.springframework.data.repository.config.excluded.MyOtherRepositoryImpl; import org.springframework.data.repository.config.stereotype.MyStereotypeRepository; import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.sample.AddressRepository; import org.springframework.data.repository.sample.AddressRepositoryClient; import org.springframework.data.repository.sample.ProductRepository; @@ -279,4 +288,43 @@ class RepositoryConfigurationDelegateUnitTests { return "commons"; } } + + @Test // GH-3074 + void registersGenericsForConstrainingRepositoryFactoryBean() { + + AnnotationMetadata metadata = AnnotationMetadata.introspect(AnnotatedBeanNamesConfig.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + RepositoryConfigurationSource source = new AnnotationRepositoryConfigurationSource(metadata, + EnableRepositories.class, context, context.getEnvironment(), + context.getDefaultListableBeanFactory(), new AnnotationBeanNameGenerator()) { + + @Override + public Optional getRepositoryFactoryBeanClassName() { + return Optional.of(IdConstrainingRepositoryFactoryBean.class.getName()); + } + }; + + RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(source, context, + context.getEnvironment()); + + List repositories = delegate.registerRepositoriesIn(context, extension); + + 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); + }); + } + + static abstract class IdConstrainingRepositoryFactoryBean, S> + extends RepositoryFactoryBeanSupport { + + protected IdConstrainingRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + } }