Skip to content

Commit da0d964

Browse files
mp911deodrotbohm
authored andcommitted
DATACMNS-1172 - Limit repository custom implementation scanning to repository interface packages.
Custom repository implementation scan uses 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. 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. Original pull request: #248.
1 parent b848d6b commit da0d964

9 files changed

+153
-5
lines changed

src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* @author Oliver Gierke
4040
* @author Mark Paluch
4141
* @author Peter Rietzler
42+
* @author Mark Paluch
4243
*/
4344
public class CustomRepositoryImplementationDetector {
4445

@@ -80,7 +81,7 @@ public AbstractBeanDefinition detectCustomImplementation(RepositoryConfiguration
8081
// TODO 2.0: Extract into dedicated interface for custom implementation lookup configuration.
8182

8283
return detectCustomImplementation(configuration.getImplementationClassName(), //
83-
configuration.getBasePackages(), //
84+
configuration.getImplementationBasePackages(configuration.getRepositoryInterface()), //
8485
configuration.getExcludeFilters());
8586
}
8687

src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.config;
1717

18+
import java.util.Collections;
19+
1820
import org.springframework.beans.factory.config.BeanDefinition;
1921
import org.springframework.core.type.filter.TypeFilter;
2022
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
@@ -26,6 +28,7 @@
2628
* Default implementation of {@link RepositoryConfiguration}.
2729
*
2830
* @author Oliver Gierke
31+
* @author Mark Paluch
2932
*/
3033
public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSource>
3134
implements RepositoryConfiguration<T> {
@@ -78,6 +81,15 @@ public Iterable<String> getBasePackages() {
7881
return configurationSource.getBasePackages();
7982
}
8083

84+
/*
85+
* (non-Javadoc)
86+
* @see org.springframework.data.repository.config.RepositoryConfiguration#getBasePackages(String)
87+
*/
88+
@Override
89+
public Iterable<String> getImplementationBasePackages(String interfaceClassName) {
90+
return Collections.singleton(ClassUtils.getPackageName(interfaceClassName));
91+
}
92+
8193
/*
8294
* (non-Javadoc)
8395
* @see org.springframework.data.repository.config.RepositoryConfiguration#getRepositoryInterface()

src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
3333
*/
3434
Iterable<String> getBasePackages();
3535

36+
/**
37+
* Returns the base packages to scan for repository implementations.
38+
*
39+
* @param interfaceClassName class name of the interface.
40+
* @return
41+
* @since 1.13.8
42+
*/
43+
Iterable<String> getImplementationBasePackages(String interfaceClassName);
44+
3645
/**
3746
* Returns the interface name of the repository.
3847
*

src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,13 +22,15 @@
2222
import org.junit.runner.RunWith;
2323
import org.mockito.Mock;
2424
import org.mockito.runners.MockitoJUnitRunner;
25+
import org.springframework.beans.factory.config.ConstructorArgumentValues;
2526
import org.springframework.beans.factory.support.RootBeanDefinition;
2627
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
2728

2829
/**
2930
* Unit tests for {@link DefaultRepositoryConfiguration}.
3031
*
3132
* @author Oliver Gierke
33+
* @author Mark Paluch
3234
*/
3335
@RunWith(MockitoJUnitRunner.class)
3436
public class DefaultRepositoryConfigurationUnitTests {
@@ -48,4 +50,39 @@ public void supportsBasicConfiguration() {
4850
assertThat(configuration.getQueryLookupStrategyKey(), is((Object) Key.CREATE_IF_NOT_FOUND));
4951
assertThat(configuration.isLazyInit(), is(false));
5052
}
53+
54+
@Test // DATACMNS-1172
55+
public void limitsImplementationBasePackages() {
56+
57+
Iterable<String> packages = getConfiguration(source).getImplementationBasePackages("com.acme.MyRepository");
58+
59+
assertThat(packages, hasItem("com.acme"));
60+
}
61+
62+
@Test // DATACMNS-1172
63+
public void limitsImplementationBasePackagesOfNestedClass() {
64+
65+
Iterable<String> packages = getConfiguration(source).getImplementationBasePackages(NestedInterface.class.getName());
66+
67+
assertThat(packages, hasItem("org.springframework.data.repository.config"));
68+
}
69+
70+
private DefaultRepositoryConfiguration<RepositoryConfigurationSource> getConfiguration(
71+
RepositoryConfigurationSource source) {
72+
RootBeanDefinition beanDefinition = createBeanDefinition();
73+
return new DefaultRepositoryConfiguration<RepositoryConfigurationSource>(source, beanDefinition);
74+
}
75+
76+
private static RootBeanDefinition createBeanDefinition() {
77+
78+
RootBeanDefinition beanDefinition = new RootBeanDefinition("com.acme.MyRepository");
79+
80+
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
81+
constructorArgumentValues.addGenericArgumentValue(MyRepository.class);
82+
beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
83+
84+
return beanDefinition;
85+
}
86+
87+
private interface NestedInterface {}
5188
}

src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.config;
1717

18-
import static org.mockito.Matchers.*;
18+
import static org.mockito.Matchers.any;
19+
import static org.mockito.Matchers.eq;
1920
import static org.mockito.Mockito.*;
2021

2122
import java.lang.annotation.Annotation;
@@ -31,12 +32,14 @@
3132
import org.springframework.core.io.DefaultResourceLoader;
3233
import org.springframework.core.type.AnnotationMetadata;
3334
import org.springframework.core.type.StandardAnnotationMetadata;
35+
import org.springframework.data.repository.config.basepackage.PersonRepositoryImpl;
3436
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
3537

3638
/**
3739
* Integration test for {@link RepositoryBeanDefinitionRegistrarSupport}.
3840
*
3941
* @author Oliver Gierke
42+
* @author Mark Paluch
4043
*/
4144
@RunWith(MockitoJUnitRunner.class)
4245
public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
@@ -66,6 +69,17 @@ public void registersBeanDefinitionForFoundBean() {
6669
assertNoBeanDefinitionRegisteredFor("profileRepository");
6770
}
6871

72+
@Test // DATACMNS-1172
73+
public void shouldLimitImplementationBasePackages() {
74+
75+
AnnotationMetadata metadata = new StandardAnnotationMetadata(LimitsImplementationBasePackages.class, true);
76+
77+
registrar.registerBeanDefinitions(metadata, registry);
78+
79+
assertBeanDefinitionRegisteredFor("personRepository");
80+
assertNoBeanDefinitionRegisteredFor("personRepositoryImpl");
81+
}
82+
6983
/**
7084
* @see DATACMNS-360
7185
*/
@@ -133,4 +147,7 @@ protected String getModulePrefix() {
133147
return "commons";
134148
}
135149
}
150+
151+
@EnableRepositories(basePackageClasses = PersonRepositoryImpl.class)
152+
static class LimitsImplementationBasePackages {}
136153
}

src/test/java/org/springframework/data/repository/config/SampleConfiguration.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,12 @@
1717

1818
import org.springframework.context.annotation.ComponentScan.Filter;
1919
import org.springframework.context.annotation.FilterType;
20+
import org.springframework.data.repository.config.basepackage.repo.PersonRepository;
2021

21-
@EnableRepositories(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyOtherRepository.class), basePackageClasses = AnnotationRepositoryConfigurationSourceUnitTests.class)
22+
@EnableRepositories(
23+
excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyOtherRepository.class),
24+
@Filter(type = FilterType.ASSIGNABLE_TYPE, value = PersonRepository.class) },
25+
basePackageClasses = AnnotationRepositoryConfigurationSourceUnitTests.class)
2226
class SampleConfiguration {
2327

2428
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.config.basepackage;
17+
18+
import org.springframework.data.repository.config.basepackage.repo.PersonRepositoryCustom;
19+
20+
/**
21+
* @author Mark Paluch
22+
*/
23+
public class PersonRepositoryImpl implements PersonRepositoryCustom {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.config.basepackage.repo;
17+
18+
import org.springframework.data.mapping.Person;
19+
import org.springframework.data.repository.Repository;
20+
21+
/**
22+
* @author Mark Paluch
23+
*/
24+
public interface PersonRepository extends Repository<Person, String>, PersonRepositoryCustom {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.config.basepackage.repo;
17+
18+
/**
19+
* @author Mark Paluch
20+
*/
21+
public interface PersonRepositoryCustom {}

0 commit comments

Comments
 (0)