Browse Source

Limit generics length to declared length.

Also, rename method to reflect what it actually returns.
Document generic usage constraints in RepositoryFactoryBeanSupport subclasses.

Closes #3089
3.2.x
Mark Paluch 2 years ago
parent
commit
e2ee018b1a
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 19
      src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java
  2. 11
      src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java
  3. 38
      src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java

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

@ -186,7 +186,7 @@ public class RepositoryConfigurationDelegate { @@ -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 { @@ -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 { @@ -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 { @@ -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));
}
/**

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

@ -46,10 +46,17 @@ import org.springframework.lang.NonNull; @@ -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

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

@ -28,6 +28,7 @@ import org.mockito.Mockito; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);
}
}
}

Loading…
Cancel
Save