diff --git a/pom.xml b/pom.xml
index 8576363d34..530c65852b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-commons
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3082-SNAPSHOT
Spring Data Core
Core Spring concepts underpinning every Spring Data module.
diff --git a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java
index d6cd8bdffd..7d042d1b13 100644
--- a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java
+++ b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java
@@ -24,6 +24,7 @@
import java.util.function.Function;
import java.util.stream.Stream;
+import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
@@ -64,6 +65,9 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
private static final String REPOSITORY_BASE_CLASS = "repositoryBaseClass";
private static final String CONSIDER_NESTED_REPOSITORIES = "considerNestedRepositories";
private static final String BOOTSTRAP_MODE = "bootstrapMode";
+ private static final String BEAN_NAME_GENERATOR = "nameGenerator";
+ private static final String INCLUDE_FILTERS = "includeFilters";
+ private static final String EXCLUDE_FILTERS = "excludeFilters";
private final AnnotationMetadata configMetadata;
private final AnnotationMetadata enableAnnotationMetadata;
@@ -97,14 +101,15 @@ public AnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Clas
* @param resourceLoader must not be {@literal null}.
* @param environment must not be {@literal null}.
* @param registry must not be {@literal null}.
- * @param generator can be {@literal null}.
+ * @param importBeanNameGenerator can be {@literal null}.
*/
public AnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Class extends Annotation> annotation,
ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry,
- @Nullable BeanNameGenerator generator) {
+ @Nullable BeanNameGenerator importBeanNameGenerator) {
super(environment, ConfigurationUtils.getRequiredClassLoader(resourceLoader), registry,
- defaultBeanNameGenerator(generator));
+ configuredOrDefaultBeanNameGenerator(metadata, annotation,
+ ConfigurationUtils.getRequiredClassLoader(resourceLoader), importBeanNameGenerator));
Assert.notNull(metadata, "Metadata must not be null");
Assert.notNull(annotation, "Annotation must not be null");
@@ -172,12 +177,12 @@ public Object getSource() {
@Override
protected Iterable getIncludeFilters() {
- return parseFilters("includeFilters");
+ return parseFilters(INCLUDE_FILTERS);
}
@Override
public Streamable getExcludeFilters() {
- return parseFilters("excludeFilters");
+ return parseFilters(EXCLUDE_FILTERS);
}
@Override
@@ -301,10 +306,28 @@ private Optional getNullDefaultedAttribute(String attributeName) {
*/
private static boolean hasExplicitFilters(AnnotationAttributes attributes) {
- return Stream.of("includeFilters", "excludeFilters") //
+ return Stream.of(INCLUDE_FILTERS, EXCLUDE_FILTERS) //
.anyMatch(it -> attributes.getAnnotationArray(it).length > 0);
}
+ private static BeanNameGenerator configuredOrDefaultBeanNameGenerator(AnnotationMetadata metadata,
+ Class extends Annotation> annotation, ClassLoader beanClassLoader,
+ @Nullable BeanNameGenerator importBeanNameGenerator) {
+
+ Map annotationAttributes = metadata.getAnnotationAttributes(annotation.getName());
+
+ if (annotationAttributes != null) {
+
+ BeanNameGenerator beanNameGenerator = getBeanNameGenerator(annotationAttributes, beanClassLoader);
+
+ if (beanNameGenerator != null) {
+ return beanNameGenerator;
+ }
+ }
+
+ return defaultBeanNameGenerator(importBeanNameGenerator);
+ }
+
/**
* Returns the {@link BeanNameGenerator} to use falling back to an {@link AnnotationBeanNameGenerator} if either the
* given generator is {@literal null} or it's the one locally declared in {@link ConfigurationClassPostProcessor}'s
@@ -312,13 +335,48 @@ private static boolean hasExplicitFilters(AnnotationAttributes attributes) {
* customized.
*
* @param generator can be {@literal null}.
- * @return
+ * @return the configured {@link BeanNameGenerator} if it is not
+ * {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} or {@link AnnotationBeanNameGenerator}
+ * otherwise.
* @since 2.2
*/
private static BeanNameGenerator defaultBeanNameGenerator(@Nullable BeanNameGenerator generator) {
return generator == null || ConfigurationClassPostProcessor.IMPORT_BEAN_NAME_GENERATOR.equals(generator) //
- ? new AnnotationBeanNameGenerator() //
+ ? AnnotationBeanNameGenerator.INSTANCE //
: generator;
}
+
+ /**
+ * Obtain a configured {@link BeanNameGenerator}.
+ *
+ * @param beanClassLoader a class loader to load the configured {@link BeanNameGenerator} class in case it was
+ * configured as String instead of a Class instance.
+ * @return the bean name generator.
+ */
+ @Nullable
+ @SuppressWarnings("unchecked")
+ private static BeanNameGenerator getBeanNameGenerator(Map annotationAttributes,
+ ClassLoader beanClassLoader) {
+
+ Object configuredBeanNameGenerator = annotationAttributes.get(BEAN_NAME_GENERATOR);
+
+ if (configuredBeanNameGenerator == null) {
+ return null;
+ }
+
+ if (configuredBeanNameGenerator instanceof String cbng) {
+ try {
+ configuredBeanNameGenerator = ClassUtils.forName(cbng, beanClassLoader);
+ } catch (Exception o_O) {
+ throw new RuntimeException(o_O);
+ }
+ }
+
+ if (configuredBeanNameGenerator != BeanNameGenerator.class) {
+ return BeanUtils.instantiateClass((Class extends BeanNameGenerator>) configuredBeanNameGenerator);
+ }
+
+ return null;
+ }
}
diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java
index f799ea49ed..50a84dd8de 100644
--- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java
+++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java
@@ -174,7 +174,7 @@ default T getRequiredAttribute(String name, Class type) {
BootstrapMode getBootstrapMode();
/**
- * Returns a human readable description of the repository configuration source for error reporting purposes.
+ * Returns a human-readable description of the repository configuration source for error reporting purposes.
*
* @return can be {@literal null}.
* @since 2.3
diff --git a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java
index aeb7d13eff..bef1116d96 100755
--- a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java
+++ b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java
@@ -24,7 +24,9 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ComponentScan.Filter;
+import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
@@ -33,6 +35,8 @@
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.config.basepackage.repo.PersonRepository;
+import org.springframework.data.repository.core.support.DummyRepositoryFactory;
/**
* Unit tests for {@link AnnotationRepositoryConfigurationSource}.
@@ -165,6 +169,17 @@ void lookupOfEmptyStringExposesAbsentValue() {
.isThrownBy(() -> source.getRequiredAttribute("namedQueriesLocation", String.class));
}
+ @Test // GH-3082
+ void considerBeanNameGenerator() {
+
+ RootBeanDefinition bd = new RootBeanDefinition(DummyRepositoryFactory.class);
+ bd.getConstructorArgumentValues().addGenericArgumentValue(PersonRepository.class);
+
+ assertThat(getConfigSource(ConfigurationWithBeanNameGenerator.class).generateBeanName(bd))
+ .isEqualTo("org.springframework.data.repository.config.basepackage.repo.PersonRepository");
+ assertThat(getConfigSource(DefaultConfiguration.class).generateBeanName(bd)).isEqualTo("personRepository");
+ }
+
private AnnotationRepositoryConfigurationSource getConfigSource(Class> type) {
AnnotationMetadata metadata = new StandardAnnotationMetadata(type, true);
@@ -186,6 +201,9 @@ static class DefaultConfigurationWithNestedRepositories {}
@EnableRepositories(excludeFilters = { @Filter(Primary.class) })
static class ConfigurationWithExplicitFilter {}
+ @EnableRepositories(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
+ static class ConfigurationWithBeanNameGenerator {}
+
@Retention(RetentionPolicy.RUNTIME)
@interface SampleAnnotation {
diff --git a/src/test/java/org/springframework/data/repository/config/EnableRepositories.java b/src/test/java/org/springframework/data/repository/config/EnableRepositories.java
index 0e3ff2ba68..806c4fc23b 100644
--- a/src/test/java/org/springframework/data/repository/config/EnableRepositories.java
+++ b/src/test/java/org/springframework/data/repository/config/EnableRepositories.java
@@ -19,6 +19,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.data.repository.PagingAndSortingRepository;
@@ -43,6 +44,8 @@
Class> repositoryBaseClass() default PagingAndSortingRepository.class;
+ Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
+
String namedQueriesLocation() default "";
String repositoryImplementationPostfix() default "Impl";