Repository type preparation now only expands type variables declared in FactoryBean.

Related: GH-3074
This commit is contained in:
Oliver Drotbohm
2024-04-09 16:47:51 +02:00
parent f2608f6302
commit 36e60d80e0
2 changed files with 59 additions and 10 deletions

View File

@@ -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<Class<?>> 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);
}
/**

View File

@@ -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<String> getRepositoryFactoryBeanClassName() {
return Optional.of(IdConstrainingRepositoryFactoryBean.class.getName());
}
};
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(source, context,
context.getEnvironment());
List<BeanComponentDefinition> 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<T extends Repository<S, UUID>, S>
extends RepositoryFactoryBeanSupport<T, S, UUID> {
protected IdConstrainingRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
}
}