Skip to content

Commit 2f43bd5

Browse files
committed
Limit generics length to declared length.
Also, rename method to reflect what it actually returns. Document generic usage constraints in RepositoryFactoryBeanSupport subclasses. Closes #3089
1 parent 18d31ec commit 2f43bd5

File tree

3 files changed

+49
-18
lines changed

3 files changed

+49
-18
lines changed

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

+8-10
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegist
186186
}
187187

188188
RootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition();
189-
beanDefinition.setTargetType(getRepositoryInterface(configuration));
189+
beanDefinition.setTargetType(getRepositoryFactoryBeanType(configuration));
190190
beanDefinition.setResourceDescription(configuration.getResourceDescription());
191191

192192
String beanName = configurationSource.generateBeanName(beanDefinition);
@@ -312,17 +312,16 @@ private static ApplicationStartup getStartup(BeanDefinitionRegistry registry) {
312312
}
313313

314314
/**
315-
* Returns the repository interface of the given {@link RepositoryConfiguration} as loaded {@link Class}.
315+
* Returns the repository factory bean type from the given {@link RepositoryConfiguration} as loaded {@link Class}.
316316
*
317317
* @param configuration must not be {@literal null}.
318318
* @return can be {@literal null}.
319319
*/
320320
@Nullable
321-
private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configuration) {
321+
private ResolvableType getRepositoryFactoryBeanType(RepositoryConfiguration<?> configuration) {
322322

323323
String interfaceName = configuration.getRepositoryInterface();
324-
ClassLoader classLoader = resourceLoader.getClassLoader() == null
325-
? ClassUtils.getDefaultClassLoader()
324+
ClassLoader classLoader = resourceLoader.getClassLoader() == null ? ClassUtils.getDefaultClassLoader()
326325
: resourceLoader.getClassLoader();
327326

328327
classLoader = classLoader != null ? classLoader : getClass().getClassLoader();
@@ -346,7 +345,7 @@ private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configu
346345
ResolvableType[] declaredGenerics = ResolvableType.forClass(factoryBean).getGenerics();
347346
ResolvableType[] parentGenerics = ResolvableType.forClass(RepositoryFactoryBeanSupport.class, factoryBean)
348347
.getGenerics();
349-
List<ResolvableType> resolvedGenerics = new ArrayList<ResolvableType>(factoryBean.getTypeParameters().length);
348+
List<ResolvableType> resolvedGenerics = new ArrayList<>(factoryBean.getTypeParameters().length);
350349

351350
for (int i = 0; i < parentGenerics.length; i++) {
352351

@@ -358,12 +357,11 @@ private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configu
358357
}
359358

360359
if (resolvedGenerics.size() < declaredGenerics.length) {
361-
for (int j = parentGenerics.length; j < declaredGenerics.length; j++) {
362-
resolvedGenerics.add(declaredGenerics[j]);
363-
}
360+
resolvedGenerics.addAll(Arrays.asList(declaredGenerics).subList(parentGenerics.length, declaredGenerics.length));
364361
}
365362

366-
return ResolvableType.forClassWithGenerics(factoryBean, resolvedGenerics.toArray(ResolvableType[]::new));
363+
return ResolvableType.forClassWithGenerics(factoryBean,
364+
resolvedGenerics.subList(0, declaredGenerics.length).toArray(ResolvableType[]::new));
367365
}
368366

369367
/**

src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@
4646
import org.springframework.util.Assert;
4747

4848
/**
49-
* Adapter for Springs {@link FactoryBean} interface to allow easy setup of repository factories via Spring
49+
* Adapter for Spring's {@link FactoryBean} interface to allow easy setup of repository factories via Spring
5050
* configuration.
51+
* <p>
52+
* Subclasses may pass-thru generics, provide a fixed domain, provide a fixed identifier type, or provide additional
53+
* generic type parameters. Type parameters must appear in the same order the ones from this class (repository type,
54+
* entity type, identifier type, additional type parameters). Using a different ordering will result in invalid type
55+
* definitions.
5156
*
52-
* @param <T> the type of the repository
57+
* @param <T> the type of the repository.
58+
* @param <S> the entity type.
59+
* @param <ID> the entity identifier type.
5360
* @author Oliver Gierke
5461
* @author Thomas Darimont
5562
* @author Mark Paluch

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

+32-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.mockito.junit.jupiter.MockitoExtension;
2929
import org.mockito.junit.jupiter.MockitoSettings;
3030
import org.mockito.quality.Strictness;
31+
3132
import org.springframework.aop.framework.Advised;
3233
import org.springframework.aot.hint.RuntimeHints;
3334
import org.springframework.beans.factory.ListableBeanFactory;
@@ -266,6 +267,16 @@ void registersGenericsForAdditionalGenericsRepositoryFactoryBean() {
266267
assertThat(it.getGeneric(3).getType()).isInstanceOf(TypeVariable.class);
267268
}
268269

270+
@Test // GH-3074
271+
void considersGenericLength() {
272+
273+
ResolvableType it = registerBeanDefinition(IdAndEntityConstrainingFactoryBean.class);
274+
275+
assertThat(it.getGenerics()).hasSize(2);
276+
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
277+
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
278+
}
279+
269280
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?> configClass) {
270281

271282
var context = new AnnotationConfigApplicationContext(configClass);
@@ -329,8 +340,8 @@ private ResolvableType registerBeanDefinition(Class<?> repositoryFactoryType) {
329340
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
330341

331342
RepositoryConfigurationSource source = new AnnotationRepositoryConfigurationSource(metadata,
332-
EnableRepositories.class, context, context.getEnvironment(),
333-
context.getDefaultListableBeanFactory(), new AnnotationBeanNameGenerator()) {
343+
EnableRepositories.class, context, context.getEnvironment(), context.getDefaultListableBeanFactory(),
344+
new AnnotationBeanNameGenerator()) {
334345

335346
@Override
336347
public Optional<String> getRepositoryFactoryBeanClassName() {
@@ -343,10 +354,8 @@ public Optional<String> getRepositoryFactoryBeanClassName() {
343354

344355
List<BeanComponentDefinition> repositories = delegate.registerRepositoriesIn(context, extension);
345356

346-
assertThat(repositories).hasSize(1).element(0)
347-
.extracting(BeanComponentDefinition::getBeanDefinition)
348-
.extracting(BeanDefinition::getResolvableType)
349-
.isNotNull();
357+
assertThat(repositories).hasSize(1).element(0).extracting(BeanComponentDefinition::getBeanDefinition)
358+
.extracting(BeanDefinition::getResolvableType).isNotNull();
350359

351360
return repositories.get(0).getBeanDefinition().getResolvableType();
352361
}
@@ -374,4 +383,21 @@ protected AdditionalGenericsRepositoryFactoryBean(Class<? extends T> repositoryI
374383
super(repositoryInterface);
375384
}
376385
}
386+
387+
static abstract class ModuleRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
388+
extends RepositoryFactoryBeanSupport<T, S, ID> {
389+
390+
protected ModuleRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
391+
super(repositoryInterface);
392+
}
393+
}
394+
395+
static abstract class IdAndEntityConstrainingFactoryBean<R extends Repository<T, String>, T extends Person>
396+
extends RepositoryFactoryBeanSupport<R, T, String> {
397+
protected IdAndEntityConstrainingFactoryBean(Class<R> repositoryInterface) {
398+
super(repositoryInterface);
399+
}
400+
401+
}
402+
377403
}

0 commit comments

Comments
 (0)