Skip to content

Commit da88163

Browse files
committed
DATACMNS-1172 - Extracted fragment setup into value object.
We now use FragmentMetadata to control the way fragement interfaces and base packages to scan are calculated. Refactored RepositoryConfiguration.getImplementationBasePackages() to not take a parameter. Original pull request: #248.
1 parent 3bae636 commit da88163

File tree

5 files changed

+107
-45
lines changed

5 files changed

+107
-45
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public Optional<AbstractBeanDefinition> detectCustomImplementation(RepositoryCon
7272
return detectCustomImplementation( //
7373
configuration.getImplementationClassName(), //
7474
configuration.getImplementationBeanName(), //
75-
configuration.getImplementationBasePackages(configuration.getImplementationClassName()), //
75+
configuration.getImplementationBasePackages(), //
7676
configuration.getExcludeFilters(), //
7777
bd -> configuration.getConfigurationSource().generateBeanName(bd));
7878
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ public Streamable<String> getBasePackages() {
7474

7575
/*
7676
* (non-Javadoc)
77-
* @see org.springframework.data.repository.config.RepositoryConfiguration#getBasePackages(String)
77+
* @see org.springframework.data.repository.config.RepositoryConfiguration#getImplementationBasePackages()
7878
*/
7979
@Override
80-
public Streamable<String> getImplementationBasePackages(String interfaceClassName) {
80+
public Streamable<String> getImplementationBasePackages() {
8181

8282
return configurationSource.shouldLimitRepositoryImplementationBasePackages()
83-
? Streamable.of(ClassUtils.getPackageName(interfaceClassName))
83+
? Streamable.of(ClassUtils.getPackageName(getRepositoryInterface()))
8484
: getBasePackages();
8585
}
8686

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

+91-33
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,21 @@
1515
*/
1616
package org.springframework.data.repository.config;
1717

18+
import lombok.Value;
19+
1820
import java.io.IOException;
1921
import java.util.Arrays;
22+
import java.util.Collections;
2023
import java.util.List;
2124
import java.util.Optional;
25+
import java.util.function.Function;
2226
import java.util.stream.Collectors;
2327
import java.util.stream.Stream;
2428

2529
import org.slf4j.Logger;
2630
import org.slf4j.LoggerFactory;
2731
import org.springframework.beans.factory.BeanDefinitionStoreException;
32+
import org.springframework.beans.factory.config.BeanDefinition;
2833
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2934
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3035
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -42,6 +47,7 @@
4247
import org.springframework.data.repository.core.support.RepositoryFragment;
4348
import org.springframework.data.repository.core.support.RepositoryFragmentsFactoryBean;
4449
import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider;
50+
import org.springframework.data.util.Optionals;
4551
import org.springframework.data.util.StreamUtils;
4652
import org.springframework.util.Assert;
4753
import org.springframework.util.ClassUtils;
@@ -127,7 +133,6 @@ public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {
127133
.rootBeanDefinition(RepositoryFragmentsFactoryBean.class);
128134

129135
List<String> fragmentBeanNames = registerRepositoryFragmentsImplementation(configuration) //
130-
.stream() //
131136
.map(RepositoryFragmentConfiguration::getFragmentBeanName) //
132137
.collect(Collectors.toList());
133138

@@ -171,41 +176,29 @@ private Optional<String> registerCustomImplementation(RepositoryConfiguration<?>
171176
});
172177
}
173178

174-
private List<RepositoryFragmentConfiguration> registerRepositoryFragmentsImplementation(
179+
private Stream<RepositoryFragmentConfiguration> registerRepositoryFragmentsImplementation(
175180
RepositoryConfiguration<?> configuration) {
176181

177182
ClassMetadata classMetadata = getClassMetadata(configuration.getRepositoryInterface());
178183

179184
return Arrays.stream(classMetadata.getInterfaceNames()) //
180-
.filter(this::isFragmentInterfaceCandidate) //
181-
.map(it -> detectRepositoryFragmentConfiguration(configuration, it)) //
182-
.filter(Optional::isPresent) //
183-
.map(Optional::get) //
185+
.filter(it -> FragmentMetadata.isCandidate(it, metadataReaderFactory)) //
186+
.map(it -> FragmentMetadata.of(it, configuration)) //
187+
.map(it -> detectRepositoryFragmentConfiguration(it)) //
188+
.flatMap(it -> Optionals.toStream(it)) //
184189
.peek(it -> potentiallyRegisterFragmentImplementation(configuration, it)) //
185-
.peek(it -> potentiallyRegisterRepositoryFragment(configuration, it)) //
186-
.collect(Collectors.toList());
187-
}
188-
189-
private boolean isFragmentInterfaceCandidate(String interfaceName) {
190-
191-
AnnotationMetadata metadata = getAnnotationMetadata(interfaceName);
192-
193-
return !metadata.hasAnnotation(NoRepositoryBean.class.getName());
190+
.peek(it -> potentiallyRegisterRepositoryFragment(configuration, it));
194191
}
195192

196193
private Optional<RepositoryFragmentConfiguration> detectRepositoryFragmentConfiguration(
197-
RepositoryConfiguration<?> configuration, String fragmentInterfaceName) {
194+
FragmentMetadata configuration) {
198195

199-
List<TypeFilter> exclusions = getExclusions(configuration);
200-
201-
String className = ClassUtils.getShortName(fragmentInterfaceName)
202-
.concat(configuration.getConfigurationSource().getRepositoryImplementationPostfix().orElse("Impl"));
196+
String className = configuration.getFragmentImplementationClassName();
203197

204198
Optional<AbstractBeanDefinition> beanDefinition = implementationDetector.detectCustomImplementation(className, null,
205-
configuration.getImplementationBasePackages(fragmentInterfaceName), exclusions,
206-
bd -> configuration.getConfigurationSource().generateBeanName(bd));
199+
configuration.getBasePackages(), configuration.getExclusions(), configuration.getBeanNameGenerator());
207200

208-
return beanDefinition.map(bd -> new RepositoryFragmentConfiguration(fragmentInterfaceName, bd));
201+
return beanDefinition.map(bd -> new RepositoryFragmentConfiguration(configuration.getFragmentInterfaceName(), bd));
209202
}
210203

211204
private void potentiallyRegisterFragmentImplementation(RepositoryConfiguration<?> repositoryConfiguration,
@@ -263,19 +256,84 @@ private ClassMetadata getClassMetadata(String className) {
263256
}
264257
}
265258

266-
private AnnotationMetadata getAnnotationMetadata(String className) {
259+
@Value(staticConstructor = "of")
260+
static class FragmentMetadata {
267261

268-
try {
269-
return metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
270-
} catch (IOException e) {
271-
throw new BeanDefinitionStoreException(String.format("Cannot parse %s metadata.", className), e);
262+
String fragmentInterfaceName;
263+
RepositoryConfiguration<?> configuration;
264+
265+
/**
266+
* Returns whether the given interface is a fragment candidate.
267+
*
268+
* @param interfaceName must not be {@literal null} or empty.
269+
* @param factory must not be {@literal null}.
270+
* @return
271+
*/
272+
public static boolean isCandidate(String interfaceName, MetadataReaderFactory factory) {
273+
274+
Assert.hasText(interfaceName, "Interface name must not be null or empty!");
275+
Assert.notNull(factory, "MetadataReaderFactory must not be null!");
276+
277+
AnnotationMetadata metadata = getAnnotationMetadata(interfaceName, factory);
278+
279+
return !metadata.hasAnnotation(NoRepositoryBean.class.getName());
272280
}
273-
}
274281

275-
private static List<TypeFilter> getExclusions(RepositoryConfiguration<?> configuration) {
282+
/**
283+
* Returns the exclusions to be used when scanning for fragment implementations.
284+
*
285+
* @return
286+
*/
287+
public List<TypeFilter> getExclusions() {
276288

277-
return Stream
278-
.concat(configuration.getExcludeFilters().stream(), Stream.of(new AnnotationTypeFilter(NoRepositoryBean.class)))//
279-
.collect(StreamUtils.toUnmodifiableList());
289+
Stream<TypeFilter> configurationExcludes = configuration.getExcludeFilters().stream();
290+
Stream<AnnotationTypeFilter> noRepositoryBeans = Stream.of(new AnnotationTypeFilter(NoRepositoryBean.class));
291+
292+
return Stream.concat(configurationExcludes, noRepositoryBeans).collect(StreamUtils.toUnmodifiableList());
293+
}
294+
295+
/**
296+
* Returns the name of the implementation class to be detected for the fragment interface.
297+
*
298+
* @return
299+
*/
300+
public String getFragmentImplementationClassName() {
301+
302+
RepositoryConfigurationSource configurationSource = configuration.getConfigurationSource();
303+
String postfix = configurationSource.getRepositoryImplementationPostfix().orElse("Impl");
304+
305+
return ClassUtils.getShortName(fragmentInterfaceName).concat(postfix);
306+
}
307+
308+
/**
309+
* Returns the base packages to be scanned to find implementations of the current fragment interface.
310+
*
311+
* @return
312+
*/
313+
public Iterable<String> getBasePackages() {
314+
315+
return configuration.getConfigurationSource().shouldLimitRepositoryImplementationBasePackages() ? //
316+
Collections.singleton(ClassUtils.getPackageName(fragmentInterfaceName)) : //
317+
configuration.getImplementationBasePackages();
318+
}
319+
320+
/**
321+
* Returns the bean name generating function to be used for the fragment.
322+
*
323+
* @return
324+
*/
325+
public Function<BeanDefinition, String> getBeanNameGenerator() {
326+
return definition -> configuration.getConfigurationSource().generateBeanName(definition);
327+
}
328+
329+
private static AnnotationMetadata getAnnotationMetadata(String className,
330+
MetadataReaderFactory metadataReaderFactory) {
331+
332+
try {
333+
return metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
334+
} catch (IOException e) {
335+
throw new BeanDefinitionStoreException(String.format("Cannot parse %s metadata.", className), e);
336+
}
337+
}
280338
}
281339
}

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
4040
/**
4141
* Returns the base packages to scan for repository implementations.
4242
*
43-
* @param interfaceClassName class name of the interface.
4443
* @return
4544
* @since 2.0
4645
*/
47-
Streamable<String> getImplementationBasePackages(String interfaceClassName);
46+
Streamable<String> getImplementationBasePackages();
4847

4948
/**
5049
* Returns the interface name of the repository.

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

+11-6
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void limitsImplementationBasePackages() {
8989

9090
when(source.shouldLimitRepositoryImplementationBasePackages()).thenReturn(true);
9191

92-
assertThat(getConfiguration(source).getImplementationBasePackages("com.acme.MyRepository"))
92+
assertThat(getConfiguration(source, "com.acme.MyRepository").getImplementationBasePackages())
9393
.containsOnly("com.acme");
9494
}
9595

@@ -98,7 +98,7 @@ public void limitsImplementationBasePackagesOfNestedClass() {
9898

9999
when(source.shouldLimitRepositoryImplementationBasePackages()).thenReturn(true);
100100

101-
assertThat(getConfiguration(source).getImplementationBasePackages(NestedInterface.class.getName()))
101+
assertThat(getConfiguration(source, NestedInterface.class.getName()).getImplementationBasePackages())
102102
.containsOnly("org.springframework.data.repository.config");
103103
}
104104

@@ -107,13 +107,18 @@ public void shouldNotLimitImplementationBasePackages() {
107107

108108
when(source.getBasePackages()).thenReturn(Streamable.of("com", "org.coyote"));
109109

110-
assertThat(getConfiguration(source).getImplementationBasePackages("com.acme.MyRepository")).contains("com",
110+
assertThat(getConfiguration(source, "com.acme.MyRepository").getImplementationBasePackages()).contains("com",
111111
"org.coyote");
112112
}
113113

114114
private DefaultRepositoryConfiguration<RepositoryConfigurationSource> getConfiguration(
115115
RepositoryConfigurationSource source) {
116-
RootBeanDefinition beanDefinition = createBeanDefinition();
116+
return getConfiguration(source, "com.acme.MyRepository");
117+
}
118+
119+
private DefaultRepositoryConfiguration<RepositoryConfigurationSource> getConfiguration(
120+
RepositoryConfigurationSource source, String repositoryInterfaceName) {
121+
RootBeanDefinition beanDefinition = createBeanDefinition(repositoryInterfaceName);
117122
return new DefaultRepositoryConfiguration<>(source, beanDefinition, extension);
118123
}
119124

@@ -123,9 +128,9 @@ private static class SimplerRepositoryConfigurationExtension extends RepositoryC
123128
String repositoryFactoryBeanClassName, modulePrefix;
124129
}
125130

126-
private static RootBeanDefinition createBeanDefinition() {
131+
private static RootBeanDefinition createBeanDefinition(String repositoryInterfaceName) {
127132

128-
RootBeanDefinition beanDefinition = new RootBeanDefinition("com.acme.MyRepository");
133+
RootBeanDefinition beanDefinition = new RootBeanDefinition(repositoryInterfaceName);
129134

130135
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
131136
constructorArgumentValues.addGenericArgumentValue(MyRepository.class);

0 commit comments

Comments
 (0)