DATACMNS-1754 - Polishing.

Revert bean name change so all nested repository interface components (repository bean, fragment bean, legacy custom implementation bean) remain prefixed with their enclosing type name.

Adopt tests. Add test for RepositoryBeanDefinitionRegistrar. Extend documentation.

Original pull request: #460.
This commit is contained in:
Mark Paluch
2020-08-10 16:21:34 +02:00
parent 2a44d2394c
commit e72470993d
8 changed files with 71 additions and 52 deletions

View File

@@ -749,10 +749,18 @@ Each Spring Data module includes a `repositories` element that lets you define a
----
====
In the preceding example, Spring is instructed to scan `com.acme.repositories` and all its sub-packages for interfaces extending `Repository` or one of its sub-interfaces. For each interface found, the infrastructure registers the persistence technology-specific `FactoryBean` to create the appropriate proxies that handle invocations of the query methods. Each bean is registered under a bean name that is derived from the interface name, so an interface of `UserRepository` would be registered under `userRepository`. The `base-package` attribute allows wildcards so that you can define a pattern of scanned packages.
In the preceding example, Spring is instructed to scan `com.acme.repositories` and all its sub-packages for interfaces extending `Repository` or one of its sub-interfaces.
For each interface found, the infrastructure registers the persistence technology-specific `FactoryBean` to create the appropriate proxies that handle invocations of the query methods.
Each bean is registered under a bean name that is derived from the interface name, so an interface of `UserRepository` would be registered under `userRepository`.
Bean names for nested repository interfaces are prefixed with their enclosing type name.
The `base-package` attribute allows wildcards so that you can define a pattern of scanned packages.
==== Using filters
By default, the infrastructure picks up every interface extending the persistence technology-specific `Repository` sub-interface located under the configured base package and creates a bean instance for it. However, you might want more fine-grained control over which interfaces have bean instances created for them. To do so, use `<include-filter />` and `<exclude-filter />` elements inside the `<repositories />` element. The semantics are exactly equivalent to the elements in Spring's context namespace. For details, see the link:{spring-framework-docs}/core.html#beans-scanning-filters[Spring reference documentation] for these elements.
By default, the infrastructure picks up every interface extending the persistence technology-specific `Repository` sub-interface located under the configured base package and creates a bean instance for it.
However, you might want more fine-grained control over which interfaces have bean instances created for them.
To do so, use `<include-filter />` and `<exclude-filter />` elements inside the `<repositories />` element.
The semantics are exactly equivalent to the elements in Spring's context namespace.
For details, see the link:{spring-framework-docs}/core.html#beans-scanning-filters[Spring reference documentation] for these elements.
For example, to exclude certain interfaces from instantiation as repository beans, you could use the following configuration:

View File

@@ -56,7 +56,8 @@ class DefaultImplementationLookupConfiguration implements ImplementationLookupCo
this.config = config;
this.interfaceName = interfaceName;
this.beanName = Introspector.decapitalize(getLocalName(interfaceName).concat(config.getImplementationPostfix()));
this.beanName = Introspector
.decapitalize(ClassUtils.getShortName(interfaceName).concat(config.getImplementationPostfix()));
}
/*
@@ -144,7 +145,7 @@ class DefaultImplementationLookupConfiguration implements ImplementationLookupCo
String localName = getLocalName(beanClassName);
return localName.equals(getImplementationClassName()) //
&& getBasePackages().stream().anyMatch(it -> beanPackage.startsWith(it));
&& getBasePackages().stream().anyMatch(beanPackage::startsWith);
}
private String getLocalName(String className) {
@@ -159,7 +160,6 @@ class DefaultImplementationLookupConfiguration implements ImplementationLookupCo
MetadataReader reader = getMetadataReaderFactory().getMetadataReader(beanClassName);
return filters.stream().anyMatch(it -> matches(it, reader));
} catch (IOException o_O) {
return true;
}

View File

@@ -131,33 +131,24 @@ class CdiRepositoryBeanUnitTests {
@Test // DATACMNS-322
void createsPassivationId() {
CdiRepositoryBean<SampleRepository> bean = new DummyCdiRepositoryBean<>( //
SINGLE_ANNOTATION, //
SampleRepository.class, //
beanManager //
CdiRepositoryBean<SampleRepository> bean = new DummyCdiRepositoryBean<>(SINGLE_ANNOTATION, SampleRepository.class,
beanManager
);
assertThat(bean.getId()).isEqualTo(PASSIVATION_ID);
}
@Test // DATACMNS-764
@Test // DATACMNS-764, DATACMNS-1754
void passesCorrectBeanNameToTheImplementationDetector() {
CustomRepositoryImplementationDetector detector = mock(CustomRepositoryImplementationDetector.class);
CdiRepositoryBean<SampleRepository> bean = new CdiRepositoryBean<SampleRepository>( //
SINGLE_ANNOTATION, //
SampleRepository.class, //
beanManager, //
Optional.of(detector) //
) {
CdiRepositoryBean<SampleRepository> bean = new CdiRepositoryBean<SampleRepository>(SINGLE_ANNOTATION,
SampleRepository.class, beanManager, Optional.of(detector)) {
@Override
protected SampleRepository create( //
CreationalContext<SampleRepository> creationalContext, //
Class<SampleRepository> repositoryType, //
Optional<Object> customImplementation //
) {
protected SampleRepository create(CreationalContext<SampleRepository> creationalContext,
Class<SampleRepository> repositoryType, Optional<Object> customImplementation) {
return null;
}
};
@@ -171,7 +162,7 @@ class CdiRepositoryBeanUnitTests {
ImplementationLookupConfiguration configuration = captor.getValue();
assertThat(configuration.getImplementationBeanName()).isEqualTo("sampleRepositoryImpl");
assertThat(configuration.getImplementationBeanName()).isEqualTo("cdiRepositoryBeanUnitTests.SampleRepositoryImpl");
assertThat(configuration.getImplementationClassName()).isEqualTo("SampleRepositoryImpl");
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository.cdi;
/**
* @author Kyrylo Merzlikin
*/
public class NestedFragmentInterfaceImpl implements RepositoryFragments.NestedFragmentInterface {
@Override
public String getKey() {
return "NestedFragmentImpl";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2020 the original author or authors.
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,4 +24,12 @@ public interface RepositoryFragments {
String getKey();
}
class NestedFragmentInterfaceImpl implements NestedFragmentInterface {
@Override
public String getKey() {
return "NestedFragmentImpl";
}
}
}

View File

@@ -82,7 +82,8 @@ class RepositoryFragmentsIntegrationTests {
void shouldFindImplementationForNestedRepositoryFragment() {
ComposedRepository repository = getBean(ComposedRepository.class);
NestedFragmentInterfaceImpl fragment = getBean(NestedFragmentInterfaceImpl.class);
RepositoryFragments.NestedFragmentInterfaceImpl fragment = getBean(
RepositoryFragments.NestedFragmentInterfaceImpl.class);
assertThat(repository.getKey()).isEqualTo("NestedFragmentImpl");
assertThat(fragment.getKey()).isEqualTo("NestedFragmentImpl");

View File

@@ -46,7 +46,7 @@ class DefaultImplementationLookupConfigurationUnitTests {
DefaultImplementationLookupConfiguration lookupConfiguration = new DefaultImplementationLookupConfiguration(idcMock,
"com.acme.Repositories$NestedRepository");
assertThat(lookupConfiguration.getImplementationBeanName()).isEqualTo("nestedRepositoryImpl");
assertThat(lookupConfiguration.getImplementationBeanName()).isEqualTo("repositories.NestedRepositoryImpl");
assertThat(lookupConfiguration.getImplementationClassName()).isEqualTo("NestedRepositoryImpl");
}

View File

@@ -36,6 +36,8 @@ import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.mapping.Person;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.basepackage.FragmentImpl;
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
@@ -76,6 +78,18 @@ class RepositoryBeanDefinitionRegistrarSupportUnitTests {
assertNoBeanDefinitionRegisteredFor("profileRepository");
}
@Test // DATACMNS-1754
void registersBeanDefinitionForNestedRepositories() {
AnnotationMetadata metadata = AnnotationMetadata.introspect(NestedRepositoriesConfiguration.class);
registrar.registerBeanDefinitions(metadata, registry);
assertBeanDefinitionRegisteredFor("repositoryBeanDefinitionRegistrarSupportUnitTests.MyNestedRepository");
assertBeanDefinitionRegisteredFor("repositoryBeanDefinitionRegistrarSupportUnitTests.NestedFragmentImpl");
assertBeanDefinitionRegisteredFor("repositoryBeanDefinitionRegistrarSupportUnitTests.MyNestedRepositoryImpl");
}
@Test // DATACMNS-1147
void registersBeanDefinitionWithoutFragmentImplementations() {
@@ -183,4 +197,28 @@ class RepositoryBeanDefinitionRegistrarSupportUnitTests {
@EnableRepositories(basePackageClasses = FragmentImpl.class)
static class LimitsImplementationBasePackages {}
@EnableRepositories(basePackageClasses = MyNestedRepository.class, considerNestedRepositories = true,
excludeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE,
value = RepositoryConfigurationExtensionSupportUnitTests.ReactiveRepository.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyOtherRepository.class) })
static class NestedRepositoriesConfiguration {}
interface MyNestedRepository extends Repository<Person, Long>, NestedFragment {
}
interface NestedFragment {
}
static class NestedFragmentImpl implements NestedFragment {
}
static class MyNestedRepositoryImpl {
}
}