Skip to content

Add support for BeanNameGenerator configuration in Enable…Repositories #3083

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.0-GH-3082-SNAPSHOT</version>

<name>Spring Data Core</name>
<description>Core Spring concepts underpinning every Spring Data module.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -172,12 +177,12 @@ public Object getSource() {

@Override
protected Iterable<TypeFilter> getIncludeFilters() {
return parseFilters("includeFilters");
return parseFilters(INCLUDE_FILTERS);
}

@Override
public Streamable<TypeFilter> getExcludeFilters() {
return parseFilters("excludeFilters");
return parseFilters(EXCLUDE_FILTERS);
}

@Override
Expand Down Expand Up @@ -301,24 +306,77 @@ private Optional<String> 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<String, Object> 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
* {@code importBeanNameGenerator}. This is to make sure we only use the given {@link BeanNameGenerator} if it was
* 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<String, Object> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ default <T> T getRequiredAttribute(String name, Class<T> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}.
Expand Down Expand Up @@ -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);
Expand All @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -43,6 +44,8 @@

Class<?> repositoryBaseClass() default PagingAndSortingRepository.class;

Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

String namedQueriesLocation() default "";

String repositoryImplementationPostfix() default "Impl";
Expand Down
Loading