diff --git a/src/main/asciidoc/repositories.adoc b/src/main/asciidoc/repositories.adoc index 2edeb8c6f..da1170c9a 100644 --- a/src/main/asciidoc/repositories.adoc +++ b/src/main/asciidoc/repositories.adoc @@ -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 `` and `` elements inside the `` 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 `` and `` elements inside the `` 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: diff --git a/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java b/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java index dfd1dac05..c321a6d60 100644 --- a/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java @@ -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; } diff --git a/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java b/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java index 96733586d..9193a8b98 100755 --- a/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java @@ -131,33 +131,24 @@ class CdiRepositoryBeanUnitTests { @Test // DATACMNS-322 void createsPassivationId() { - CdiRepositoryBean bean = new DummyCdiRepositoryBean<>( // - SINGLE_ANNOTATION, // - SampleRepository.class, // - beanManager // + CdiRepositoryBean 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 bean = new CdiRepositoryBean( // - SINGLE_ANNOTATION, // - SampleRepository.class, // - beanManager, // - Optional.of(detector) // - ) { + CdiRepositoryBean bean = new CdiRepositoryBean(SINGLE_ANNOTATION, + SampleRepository.class, beanManager, Optional.of(detector)) { @Override - protected SampleRepository create( // - CreationalContext creationalContext, // - Class repositoryType, // - Optional customImplementation // - ) { + protected SampleRepository create(CreationalContext creationalContext, + Class repositoryType, Optional 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"); } diff --git a/src/test/java/org/springframework/data/repository/cdi/NestedFragmentInterfaceImpl.java b/src/test/java/org/springframework/data/repository/cdi/NestedFragmentInterfaceImpl.java deleted file mode 100644 index be5b02215..000000000 --- a/src/test/java/org/springframework/data/repository/cdi/NestedFragmentInterfaceImpl.java +++ /dev/null @@ -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"; - } -} diff --git a/src/test/java/org/springframework/data/repository/cdi/RepositoryFragments.java b/src/test/java/org/springframework/data/repository/cdi/RepositoryFragments.java index 9b520e1d2..bf82faaff 100644 --- a/src/test/java/org/springframework/data/repository/cdi/RepositoryFragments.java +++ b/src/test/java/org/springframework/data/repository/cdi/RepositoryFragments.java @@ -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"; + } + } } diff --git a/src/test/java/org/springframework/data/repository/cdi/RepositoryFragmentsIntegrationTests.java b/src/test/java/org/springframework/data/repository/cdi/RepositoryFragmentsIntegrationTests.java index 1962921b1..32433f7d1 100644 --- a/src/test/java/org/springframework/data/repository/cdi/RepositoryFragmentsIntegrationTests.java +++ b/src/test/java/org/springframework/data/repository/cdi/RepositoryFragmentsIntegrationTests.java @@ -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"); diff --git a/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java b/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java index 5da3df4e2..01f56c348 100644 --- a/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java @@ -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"); } diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java index 299f3dbd2..0ee7df39a 100755 --- a/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java @@ -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, NestedFragment { + + } + + interface NestedFragment { + + } + + static class NestedFragmentImpl implements NestedFragment { + + } + + static class MyNestedRepositoryImpl { + + } + }