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.
This commit is contained in:
@@ -343,19 +343,28 @@ public class RepositoryConfigurationDelegate {
|
||||
RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface);
|
||||
List<Class<?>> 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<ResolvableType> resolvedGenerics = new ArrayList<ResolvableType>(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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String> 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<T extends Repository<S, UUID>, S>
|
||||
@@ -327,4 +358,20 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
super(repositoryInterface);
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class DomainTypeConstrainingRepositoryFactoryBean<T extends Repository<Person, ID>, ID>
|
||||
extends RepositoryFactoryBeanSupport<T, Person, ID> {
|
||||
|
||||
protected DomainTypeConstrainingRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
|
||||
super(repositoryInterface);
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class AdditionalGenericsRepositoryFactoryBean<T extends Repository<S, ID>, S, ID, R>
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
protected AdditionalGenericsRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
|
||||
super(repositoryInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user