DATACMNS-1172 - Limit repository custom implementation scanning by default to repository interface packages.
Custom repository implementation scan defaults to the repository interface package and its subpackages and no longer scans all configured base packages. Scan for fragment implementations defaults to the fragment interface package. Using the interface package for scanning aligns the behavior with the documentation.
This default can be changed with @Enable…Repositories annotations that support the limitImplementationBasePackages parameter to scan in repository base packages for custom implementations:
@EnableJpaRepositories(limitImplementationBasePackages = false)
@Configuration
class AppConfiguration {
// …
}
Declaring the implementation along with the interface in the same package is an established design pattern allowing to limit the scanning scope. A limited scope improves scanning performance as it can skip elements on the classpath, that do not provide that particular package.
Previously, we scanned for implementations using the configured base packages that were also used to discover repository interfaces. These base packages can be broader for applications that spread repository interfaces across multiple packages.
This commit is contained in:
committed by
Oliver Gierke
parent
2220bed116
commit
3bae636e2f
@@ -73,8 +73,9 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
|
||||
|
||||
Streamable<BeanDefinition> candidates = source.getCandidates(new DefaultResourceLoader());
|
||||
|
||||
assertThat(candidates).hasSize(2).extracting("beanClassName").containsOnly(MyRepository.class.getName(),
|
||||
ComposedRepository.class.getName());
|
||||
assertThat(candidates).extracting("beanClassName")
|
||||
.contains(MyRepository.class.getName(), ComposedRepository.class.getName())
|
||||
.doesNotContain(MyOtherRepository.class.getName(), ExcludedRepository.class.getName());
|
||||
}
|
||||
|
||||
@Test // DATACMNS-47
|
||||
@@ -102,6 +103,14 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
|
||||
assertThat(source.shouldConsiderNestedRepositories()).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void returnsLimitImplementationBasePackages() {
|
||||
|
||||
assertThat(getConfigSource(DefaultConfiguration.class).shouldLimitRepositoryImplementationBasePackages()).isTrue();
|
||||
assertThat(getConfigSource(DefaultConfigurationWithoutBasePackageLimit.class)
|
||||
.shouldLimitRepositoryImplementationBasePackages()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-456
|
||||
public void findsStringAttributeByName() {
|
||||
|
||||
@@ -155,6 +164,9 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
|
||||
@EnableRepositories(considerNestedRepositories = true)
|
||||
static class DefaultConfigurationWithNestedRepositories {}
|
||||
|
||||
@EnableRepositories(limitImplementationBasePackages = false)
|
||||
static class DefaultConfigurationWithoutBasePackageLimit {}
|
||||
|
||||
@EnableRepositories(excludeFilters = { @Filter(Primary.class) })
|
||||
static class ConfigurationWithExplicitFilter {}
|
||||
|
||||
|
||||
@@ -34,19 +34,20 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.util.Streamable;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultRepositoryConfiguration}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultRepositoryConfigurationUnitTests {
|
||||
|
||||
@Mock RepositoryConfigurationSource source;
|
||||
|
||||
BeanDefinition definition = new RootBeanDefinition("com.acme.MyRepository");
|
||||
RepositoryConfigurationExtension extension = new SimplerRepositoryConfigurationExtension("factory", "module");
|
||||
|
||||
@Before
|
||||
@@ -83,6 +84,33 @@ public class DefaultRepositoryConfigurationUnitTests {
|
||||
assertThat(getConfiguration(source).getRepositoryFactoryBeanClassName()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void limitsImplementationBasePackages() {
|
||||
|
||||
when(source.shouldLimitRepositoryImplementationBasePackages()).thenReturn(true);
|
||||
|
||||
assertThat(getConfiguration(source).getImplementationBasePackages("com.acme.MyRepository"))
|
||||
.containsOnly("com.acme");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void limitsImplementationBasePackagesOfNestedClass() {
|
||||
|
||||
when(source.shouldLimitRepositoryImplementationBasePackages()).thenReturn(true);
|
||||
|
||||
assertThat(getConfiguration(source).getImplementationBasePackages(NestedInterface.class.getName()))
|
||||
.containsOnly("org.springframework.data.repository.config");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void shouldNotLimitImplementationBasePackages() {
|
||||
|
||||
when(source.getBasePackages()).thenReturn(Streamable.of("com", "org.coyote"));
|
||||
|
||||
assertThat(getConfiguration(source).getImplementationBasePackages("com.acme.MyRepository")).contains("com",
|
||||
"org.coyote");
|
||||
}
|
||||
|
||||
private DefaultRepositoryConfiguration<RepositoryConfigurationSource> getConfiguration(
|
||||
RepositoryConfigurationSource source) {
|
||||
RootBeanDefinition beanDefinition = createBeanDefinition();
|
||||
@@ -105,4 +133,6 @@ public class DefaultRepositoryConfigurationUnitTests {
|
||||
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private interface NestedInterface {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
@@ -49,4 +49,6 @@ public @interface EnableRepositories {
|
||||
String repositoryImplementationPostfix() default "Impl";
|
||||
|
||||
boolean considerNestedRepositories() default false;
|
||||
|
||||
boolean limitImplementationBasePackages() default true;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ 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.repository.config.basepackage.FragmentImpl;
|
||||
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
|
||||
|
||||
/**
|
||||
@@ -84,6 +85,28 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
assertNoBeanDefinitionRegisteredFor("excludedRepositoryImpl");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void shouldLimitImplementationBasePackages() {
|
||||
|
||||
AnnotationMetadata metadata = new StandardAnnotationMetadata(LimitsImplementationBasePackages.class, true);
|
||||
|
||||
registrar.registerBeanDefinitions(metadata, registry);
|
||||
|
||||
assertBeanDefinitionRegisteredFor("personRepository");
|
||||
assertNoBeanDefinitionRegisteredFor("fragmentImpl");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1172
|
||||
public void shouldNotLimitImplementationBasePackages() {
|
||||
|
||||
AnnotationMetadata metadata = new StandardAnnotationMetadata(UnlimitedImplementationBasePackages.class, true);
|
||||
|
||||
registrar.registerBeanDefinitions(metadata, registry);
|
||||
|
||||
assertBeanDefinitionRegisteredFor("personRepository");
|
||||
assertBeanDefinitionRegisteredFor("fragmentImpl");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-360
|
||||
public void registeredProfileRepositoriesIfProfileActivated() {
|
||||
|
||||
@@ -152,7 +175,11 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
@EnableRepositories(
|
||||
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = RepositoryWithFragmentExclusion.class),
|
||||
basePackageClasses = RepositoryWithFragmentExclusion.class)
|
||||
static class FragmentExclusionConfiguration {
|
||||
static class FragmentExclusionConfiguration {}
|
||||
|
||||
}
|
||||
@EnableRepositories(basePackageClasses = FragmentImpl.class)
|
||||
static class LimitsImplementationBasePackages {}
|
||||
|
||||
@EnableRepositories(basePackageClasses = FragmentImpl.class, limitImplementationBasePackages = false)
|
||||
static class UnlimitedImplementationBasePackages {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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.config.basepackage;
|
||||
|
||||
import org.springframework.data.repository.config.basepackage.repo.Fragment;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class FragmentImpl implements Fragment {}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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.config.basepackage.repo;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface Fragment {}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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.config.basepackage.repo;
|
||||
|
||||
import org.springframework.data.mapping.Person;
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface PersonRepository extends Repository<Person, String>, Fragment {}
|
||||
Reference in New Issue
Block a user