Skip to content

Commit d22bb69

Browse files
committed
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.
1 parent 36e60d8 commit d22bb69

File tree

2 files changed

+71
-15
lines changed

2 files changed

+71
-15
lines changed

src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -343,19 +343,28 @@ private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configu
343343
RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface);
344344
List<Class<?>> types = List.of(repositoryInterface, metadata.getDomainType(), metadata.getIdType());
345345

346-
ResolvableType factoryBeanType = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean);
347-
ResolvableType[] factoryGenerics = factoryBeanType.getGenerics();
346+
ResolvableType[] declaredGenerics = ResolvableType.forClass(factoryBean).getGenerics();
347+
ResolvableType[] parentGenerics = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean)
348+
.getGenerics();
349+
List<ResolvableType> resolvedGenerics = new ArrayList<ResolvableType>(factoryBean.getTypeParameters().length);
348350

349-
for (int i = 0; i < factoryGenerics.length; i++) {
350351

351-
ResolvableType parameter = factoryGenerics[i];
352+
for (int i = 0; i < parentGenerics.length; i++) {
352353

353-
if (parameter.getType() instanceof TypeVariable<?> && i < types.size()) {
354-
factoryGenerics[i] = ResolvableType.forClass(types.get(i));
354+
ResolvableType parameter = parentGenerics[i];
355+
356+
if (parameter.getType() instanceof TypeVariable<?>) {
357+
resolvedGenerics.add(i < types.size() ? ResolvableType.forClass(types.get(i)) : parameter);
358+
}
359+
}
360+
361+
if (resolvedGenerics.size() < declaredGenerics.length) {
362+
for (int j = parentGenerics.length; j < declaredGenerics.length; j++) {
363+
resolvedGenerics.add(declaredGenerics[j]);
355364
}
356365
}
357366

358-
return ResolvableType.forClassWithGenerics(factoryBean, factoryGenerics);
367+
return ResolvableType.forClassWithGenerics(factoryBean, resolvedGenerics.toArray(ResolvableType[]::new));
359368
}
360369

361370
/**

src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java

+55-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import java.lang.reflect.TypeVariable;
2021
import java.util.List;
2122
import java.util.Optional;
2223
import java.util.UUID;
@@ -39,6 +40,7 @@
3940
import org.springframework.context.annotation.ComponentScan.Filter;
4041
import org.springframework.context.annotation.FilterType;
4142
import org.springframework.context.support.GenericApplicationContext;
43+
import org.springframework.core.ResolvableType;
4244
import org.springframework.core.env.StandardEnvironment;
4345
import org.springframework.core.metrics.ApplicationStartup;
4446
import org.springframework.core.type.AnnotationMetadata;
@@ -232,6 +234,38 @@ void registersAotPostProcessorForDifferentConfigurations() {
232234
assertThat(context.getBeanNamesForType(RepositoryRegistrationAotProcessor.class)).hasSize(2);
233235
}
234236

237+
@Test // GH-3074
238+
void registersGenericsForIdConstrainingRepositoryFactoryBean() {
239+
240+
ResolvableType it = registerBeanDefinition(IdConstrainingRepositoryFactoryBean.class);
241+
242+
assertThat(it.getGenerics()).hasSize(2);
243+
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
244+
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
245+
}
246+
247+
@Test // GH-3074
248+
void registersGenericsForDomainTypeConstrainingRepositoryFactoryBean() {
249+
250+
ResolvableType it = registerBeanDefinition(DomainTypeConstrainingRepositoryFactoryBean.class);
251+
252+
assertThat(it.getGenerics()).hasSize(2);
253+
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
254+
assertThat(it.getGeneric(1).resolve()).isEqualTo(String.class);
255+
}
256+
257+
@Test // GH-3074
258+
void registersGenericsForAdditionalGenericsRepositoryFactoryBean() {
259+
260+
ResolvableType it = registerBeanDefinition(AdditionalGenericsRepositoryFactoryBean.class);
261+
262+
assertThat(it.getGenerics()).hasSize(4);
263+
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
264+
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
265+
assertThat(it.getGeneric(2).resolve()).isEqualTo(String.class);
266+
assertThat(it.getGeneric(3).getType()).isInstanceOf(TypeVariable.class);
267+
}
268+
235269
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?> configClass) {
236270

237271
var context = new AnnotationConfigApplicationContext(configClass);
@@ -289,8 +323,7 @@ protected String getModulePrefix() {
289323
}
290324
}
291325

292-
@Test // GH-3074
293-
void registersGenericsForConstrainingRepositoryFactoryBean() {
326+
private ResolvableType registerBeanDefinition(Class<?> repositoryFactoryType) {
294327

295328
AnnotationMetadata metadata = AnnotationMetadata.introspect(AnnotatedBeanNamesConfig.class);
296329
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@@ -301,7 +334,7 @@ void registersGenericsForConstrainingRepositoryFactoryBean() {
301334

302335
@Override
303336
public Optional<String> getRepositoryFactoryBeanClassName() {
304-
return Optional.of(IdConstrainingRepositoryFactoryBean.class.getName());
337+
return Optional.of(repositoryFactoryType.getName());
305338
}
306339
};
307340

@@ -313,11 +346,9 @@ public Optional<String> getRepositoryFactoryBeanClassName() {
313346
assertThat(repositories).hasSize(1).element(0)
314347
.extracting(BeanComponentDefinition::getBeanDefinition)
315348
.extracting(BeanDefinition::getResolvableType)
316-
.satisfies(it -> {
317-
assertThat(it.getGenerics()).hasSize(2);
318-
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
319-
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
320-
});
349+
.isNotNull();
350+
351+
return repositories.get(0).getBeanDefinition().getResolvableType();
321352
}
322353

323354
static abstract class IdConstrainingRepositoryFactoryBean<T extends Repository<S, UUID>, S>
@@ -327,4 +358,20 @@ protected IdConstrainingRepositoryFactoryBean(Class<? extends T> repositoryInter
327358
super(repositoryInterface);
328359
}
329360
}
361+
362+
static abstract class DomainTypeConstrainingRepositoryFactoryBean<T extends Repository<Person, ID>, ID>
363+
extends RepositoryFactoryBeanSupport<T, Person, ID> {
364+
365+
protected DomainTypeConstrainingRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
366+
super(repositoryInterface);
367+
}
368+
}
369+
370+
static abstract class AdditionalGenericsRepositoryFactoryBean<T extends Repository<S, ID>, S, ID, R>
371+
extends RepositoryFactoryBeanSupport<T, S, ID> {
372+
373+
protected AdditionalGenericsRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
374+
super(repositoryInterface);
375+
}
376+
}
330377
}

0 commit comments

Comments
 (0)