Limit generics length to declared length.
Also, rename method to reflect what it actually returns. Document generic usage constraints in RepositoryFactoryBeanSupport subclasses. Closes #3089
This commit is contained in:
@@ -186,7 +186,7 @@ public class RepositoryConfigurationDelegate {
|
||||
}
|
||||
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition();
|
||||
beanDefinition.setTargetType(getRepositoryInterface(configuration));
|
||||
beanDefinition.setTargetType(getRepositoryFactoryBeanType(configuration));
|
||||
beanDefinition.setResourceDescription(configuration.getResourceDescription());
|
||||
|
||||
String beanName = configurationSource.generateBeanName(beanDefinition);
|
||||
@@ -312,17 +312,16 @@ public class RepositoryConfigurationDelegate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@@ -346,8 +345,7 @@ public class RepositoryConfigurationDelegate {
|
||||
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++) {
|
||||
|
||||
@@ -359,12 +357,11 @@ public class RepositoryConfigurationDelegate {
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,10 +46,17 @@ import org.springframework.lang.NonNull;
|
||||
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
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.mockito.Mockito;
|
||||
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;
|
||||
@@ -266,6 +267,16 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
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);
|
||||
@@ -329,8 +340,8 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
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() {
|
||||
@@ -343,10 +354,8 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -374,4 +383,21 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user