From d22bb69a46b326548625a0cc2772e9a2130ed65e Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 9 Apr 2024 19:20:28 +0200 Subject: [PATCH] Support advanced generics redeclarations in RepositoryFactoryBeanSupport extensions. Spring Data modules might override, and, by that, fix some of the generic type parameters exposed by RepositoryFactoryBeanSupport. We now more thoroughly walk through them to consider the ones expanded already and automatically expand the remaining ones with either the types found on the user repository interface or the unresolved type variable. Ticket: GH-3074. --- .../RepositoryConfigurationDelegate.java | 23 ++++--- ...ositoryConfigurationDelegateUnitTests.java | 63 ++++++++++++++++--- 2 files changed, 71 insertions(+), 15 deletions(-) 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 6cdda4a04..140dde8bb 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 @@ public class RepositoryConfigurationDelegate { 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); } } - return ResolvableType.forClassWithGenerics(factoryBean, factoryGenerics); + if (resolvedGenerics.size() < declaredGenerics.length) { + for (int j = parentGenerics.length; j < declaredGenerics.length; j++) { + resolvedGenerics.add(declaredGenerics[j]); + } + } + + 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 5f7907fbc..71a13568d 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 @@ package org.springframework.data.repository.config; 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; 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 @@ class RepositoryConfigurationDelegateUnitTests { 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 @@ class RepositoryConfigurationDelegateUnitTests { } } - @Test // GH-3074 - void registersGenericsForConstrainingRepositoryFactoryBean() { + private ResolvableType registerBeanDefinition(Class repositoryFactoryType) { AnnotationMetadata metadata = AnnotationMetadata.introspect(AnnotatedBeanNamesConfig.class); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -301,7 +334,7 @@ class RepositoryConfigurationDelegateUnitTests { @Override public Optional getRepositoryFactoryBeanClassName() { - return Optional.of(IdConstrainingRepositoryFactoryBean.class.getName()); + return Optional.of(repositoryFactoryType.getName()); } }; @@ -313,11 +346,9 @@ class RepositoryConfigurationDelegateUnitTests { 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 @@ class RepositoryConfigurationDelegateUnitTests { 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); + } + } }