Skip to content

Commit

Permalink
Limit generics length to declared length.
Browse files Browse the repository at this point in the history
Also, rename method to reflect what it actually returns.
Document generic usage constraints in RepositoryFactoryBeanSupport subclasses.

Closes #3089
  • Loading branch information
mp911de committed May 8, 2024
1 parent a08a5f7 commit e2ee018
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegist
}

RootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition();
beanDefinition.setTargetType(getRepositoryInterface(configuration));
beanDefinition.setTargetType(getRepositoryFactoryBeanType(configuration));
beanDefinition.setResourceDescription(configuration.getResourceDescription());

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

/**
* Returns the repository interface of the given {@link RepositoryConfiguration} as loaded {@link Class}.
* Returns the repository factory bean type from the given {@link RepositoryConfiguration} as loaded {@link Class}.
*
* @param configuration must not be {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configuration) {
private ResolvableType getRepositoryFactoryBeanType(RepositoryConfiguration<?> configuration) {

String interfaceName = configuration.getRepositoryInterface();
ClassLoader classLoader = resourceLoader.getClassLoader() == null
? ClassUtils.getDefaultClassLoader()
ClassLoader classLoader = resourceLoader.getClassLoader() == null ? ClassUtils.getDefaultClassLoader()
: resourceLoader.getClassLoader();

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

List<ResolvableType> resolvedGenerics = new ArrayList<>(factoryBean.getTypeParameters().length);

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

Expand All @@ -359,12 +357,11 @@ private ResolvableType getRepositoryInterface(RepositoryConfiguration<?> configu
}

if (resolvedGenerics.size() < declaredGenerics.length) {
for (int j = parentGenerics.length; j < declaredGenerics.length; j++) {
resolvedGenerics.add(declaredGenerics[j]);
}
resolvedGenerics.addAll(Arrays.asList(declaredGenerics).subList(parentGenerics.length, declaredGenerics.length));
}

return ResolvableType.forClassWithGenerics(factoryBean, resolvedGenerics.toArray(ResolvableType[]::new));
return ResolvableType.forClassWithGenerics(factoryBean,
resolvedGenerics.subList(0, declaredGenerics.length).toArray(ResolvableType[]::new));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,17 @@
import org.springframework.util.Assert;

/**
* Adapter for Springs {@link FactoryBean} interface to allow easy setup of repository factories via Spring
* Adapter for Spring's {@link FactoryBean} interface to allow easy setup of repository factories via Spring
* configuration.
* <p>
* Subclasses may pass-thru generics, provide a fixed domain, provide a fixed identifier type, or provide additional
* generic type parameters. Type parameters must appear in the same order the ones from this class (repository type,
* entity type, identifier type, additional type parameters). Using a different ordering will result in invalid type
* definitions.
*
* @param <T> the type of the repository
* @param <T> the type of the repository.
* @param <S> the entity type.
* @param <ID> the entity identifier type.
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import org.springframework.aop.framework.Advised;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.ListableBeanFactory;
Expand Down Expand Up @@ -266,6 +267,16 @@ void registersGenericsForAdditionalGenericsRepositoryFactoryBean() {
assertThat(it.getGeneric(3).getType()).isInstanceOf(TypeVariable.class);
}

@Test // GH-3074
void considersGenericLength() {

ResolvableType it = registerBeanDefinition(IdAndEntityConstrainingFactoryBean.class);

assertThat(it.getGenerics()).hasSize(2);
assertThat(it.getGeneric(0).resolve()).isEqualTo(MyAnnotatedRepository.class);
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
}

private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?> configClass) {

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

RepositoryConfigurationSource source = new AnnotationRepositoryConfigurationSource(metadata,
EnableRepositories.class, context, context.getEnvironment(),
context.getDefaultListableBeanFactory(), new AnnotationBeanNameGenerator()) {
EnableRepositories.class, context, context.getEnvironment(), context.getDefaultListableBeanFactory(),
new AnnotationBeanNameGenerator()) {

@Override
public Optional<String> getRepositoryFactoryBeanClassName() {
Expand All @@ -343,10 +354,8 @@ public Optional<String> getRepositoryFactoryBeanClassName() {

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

assertThat(repositories).hasSize(1).element(0)
.extracting(BeanComponentDefinition::getBeanDefinition)
.extracting(BeanDefinition::getResolvableType)
.isNotNull();
assertThat(repositories).hasSize(1).element(0).extracting(BeanComponentDefinition::getBeanDefinition)
.extracting(BeanDefinition::getResolvableType).isNotNull();

return repositories.get(0).getBeanDefinition().getResolvableType();
}
Expand Down Expand Up @@ -374,4 +383,21 @@ protected AdditionalGenericsRepositoryFactoryBean(Class<? extends T> repositoryI
super(repositoryInterface);
}
}

static abstract class ModuleRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
extends RepositoryFactoryBeanSupport<T, S, ID> {

protected ModuleRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
}

static abstract class IdAndEntityConstrainingFactoryBean<R extends Repository<T, String>, T extends Person>
extends RepositoryFactoryBeanSupport<R, T, String> {
protected IdAndEntityConstrainingFactoryBean(Class<R> repositoryInterface) {
super(repositoryInterface);
}

}

}

0 comments on commit e2ee018

Please sign in to comment.