DATACMNS-764 - Java 8 polishing.
Use Streamable in configuration APIs for more efficient, lazy traversal of source lists. Introduced SelectionSet.of(…) to move away from constructors. Avoid the use of null in SelectionSet. Original pull request: #201.
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.data.repository.config;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -37,6 +36,7 @@ import org.springframework.core.type.filter.AspectJTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -113,7 +113,7 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBasePackages()
|
||||
*/
|
||||
public Iterable<String> getBasePackages() {
|
||||
public Streamable<String> getBasePackages() {
|
||||
|
||||
String[] value = attributes.getStringArray("value");
|
||||
String[] basePackages = attributes.getStringArray(BASE_PACKAGES);
|
||||
@@ -121,19 +121,20 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
|
||||
|
||||
// Default configuration - return package of annotated class
|
||||
if (value.length == 0 && basePackages.length == 0 && basePackageClasses.length == 0) {
|
||||
|
||||
String className = configMetadata.getClassName();
|
||||
return Collections.singleton(ClassUtils.getPackageName(className));
|
||||
return Streamable.of(ClassUtils.getPackageName(className));
|
||||
}
|
||||
|
||||
Set<String> packages = new HashSet<>();
|
||||
packages.addAll(Arrays.asList(value));
|
||||
packages.addAll(Arrays.asList(basePackages));
|
||||
|
||||
for (Class<?> typeName : basePackageClasses) {
|
||||
packages.add(ClassUtils.getPackageName(typeName));
|
||||
}
|
||||
Arrays.stream(basePackageClasses)//
|
||||
.map(ClassUtils::getPackageName)//
|
||||
.forEach(it -> packages.add(it));
|
||||
|
||||
return packages;
|
||||
return Streamable.of(packages);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -182,20 +183,15 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getExcludeFilters()
|
||||
*/
|
||||
@Override
|
||||
public Iterable<TypeFilter> getExcludeFilters() {
|
||||
public Streamable<TypeFilter> getExcludeFilters() {
|
||||
return parseFilters("excludeFilters");
|
||||
}
|
||||
|
||||
private Set<TypeFilter> parseFilters(String attributeName) {
|
||||
private Streamable<TypeFilter> parseFilters(String attributeName) {
|
||||
|
||||
Set<TypeFilter> result = new HashSet<>();
|
||||
AnnotationAttributes[] filters = attributes.getAnnotationArray(attributeName);
|
||||
|
||||
for (AnnotationAttributes filter : filters) {
|
||||
result.addAll(typeFiltersFor(filter));
|
||||
}
|
||||
|
||||
return result;
|
||||
return Streamable.of(() -> Arrays.stream(filters).flatMap(it -> typeFiltersFor(it).stream()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,16 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
@@ -37,6 +33,7 @@ import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -52,6 +49,7 @@ import org.springframework.util.Assert;
|
||||
public class CustomRepositoryImplementationDetector {
|
||||
|
||||
private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class";
|
||||
private static final String AMBIGUOUS_CUSTOM_IMPLEMENTATIONS = "Ambiguous custom implementations detected! Found %s but expected a single implementation!";
|
||||
|
||||
private final @NonNull MetadataReaderFactory metadataReaderFactory;
|
||||
private final @NonNull Environment environment;
|
||||
@@ -94,12 +92,10 @@ public class CustomRepositoryImplementationDetector {
|
||||
|
||||
Set<BeanDefinition> definitions = findCandidateBeanDefinitions(className, basePackages, excludeFilters);
|
||||
|
||||
SelectionSet<BeanDefinition> selection = new SelectionSet<>( //
|
||||
definitions, //
|
||||
c -> c.isEmpty() ? null : throwAmbiguousCustomImplementationException(c) //
|
||||
).filterIfNecessary(bd -> beanName != null && beanName.equals(beanNameGenerator.apply(bd)));
|
||||
|
||||
return Optional.ofNullable((AbstractBeanDefinition) selection.uniqueResult());
|
||||
return SelectionSet //
|
||||
.of(definitions, c -> c.isEmpty() ? Optional.empty() : throwAmbiguousCustomImplementationException(c)) //
|
||||
.filterIfNecessary(bd -> beanName != null && beanName.equals(beanNameGenerator.apply(bd)))//
|
||||
.uniqueResult().map(it -> AbstractBeanDefinition.class.cast(it));
|
||||
}
|
||||
|
||||
Set<BeanDefinition> findCandidateBeanDefinitions(String className, Iterable<String> basePackages,
|
||||
@@ -116,31 +112,20 @@ public class CustomRepositoryImplementationDetector {
|
||||
provider.setMetadataReaderFactory(metadataReaderFactory);
|
||||
provider.addIncludeFilter(new RegexPatternTypeFilter(pattern));
|
||||
|
||||
for (TypeFilter excludeFilter : excludeFilters) {
|
||||
provider.addExcludeFilter(excludeFilter);
|
||||
}
|
||||
excludeFilters.forEach(it -> provider.addExcludeFilter(it));
|
||||
|
||||
Set<BeanDefinition> definitions = new HashSet<>();
|
||||
|
||||
for (String basePackage : basePackages) {
|
||||
definitions.addAll(provider.findCandidateComponents(basePackage));
|
||||
}
|
||||
|
||||
return definitions;
|
||||
return Streamable.of(basePackages).stream()//
|
||||
.flatMap(it -> provider.findCandidateComponents(it).stream())//
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static AbstractBeanDefinition throwAmbiguousCustomImplementationException(
|
||||
private static Optional<BeanDefinition> throwAmbiguousCustomImplementationException(
|
||||
Collection<BeanDefinition> definitions) {
|
||||
|
||||
List<String> implementationClassNames = new ArrayList<String>();
|
||||
for (BeanDefinition bean : definitions) {
|
||||
implementationClassNames.add(bean.getBeanClassName());
|
||||
}
|
||||
String implementationNames = definitions.stream()//
|
||||
.map(BeanDefinition::getBeanClassName)//
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
throw new IllegalStateException(
|
||||
String.format("Ambiguous custom implementations detected! Found %s but expected a single implementation!", //
|
||||
definitions.stream()//
|
||||
.map(BeanDefinition::getBeanClassName)//
|
||||
.collect(Collectors.joining(", "))));
|
||||
throw new IllegalStateException(String.format(AMBIGUOUS_CUSTOM_IMPLEMENTATIONS, implementationNames));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
|
||||
@@ -32,16 +33,13 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class RepositoryBeanNameGenerator {
|
||||
|
||||
private static final BeanNameGenerator DELEGATE = new AnnotationBeanNameGenerator();
|
||||
|
||||
private final ClassLoader beanClassLoader;
|
||||
|
||||
public RepositoryBeanNameGenerator(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a bean name for the given bean definition.
|
||||
*
|
||||
@@ -66,11 +64,13 @@ public class RepositoryBeanNameGenerator {
|
||||
private Class<?> getRepositoryInterfaceFrom(BeanDefinition beanDefinition) {
|
||||
|
||||
if (beanDefinition instanceof ScannedGenericBeanDefinition) {
|
||||
|
||||
try {
|
||||
return ((ScannedGenericBeanDefinition) beanDefinition).resolveBeanClass(beanClassLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Could not resolve bean class.", e);
|
||||
}
|
||||
|
||||
} else {
|
||||
return getRepositoryInterfaceFromFactory(beanDefinition);
|
||||
}
|
||||
@@ -81,8 +81,11 @@ public class RepositoryBeanNameGenerator {
|
||||
Object value = beanDefinition.getConstructorArgumentValues().getArgumentValue(0, Class.class).getValue();
|
||||
|
||||
if (value instanceof Class<?>) {
|
||||
|
||||
return (Class<?>) value;
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
return ClassUtils.forName(value.toString(), beanClassLoader);
|
||||
} catch (Exception o_O) {
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.util.Streamable;
|
||||
|
||||
/**
|
||||
* Interface containing the configurable options for the Spring Data repository subsystem.
|
||||
@@ -46,7 +46,7 @@ public interface RepositoryConfigurationSource {
|
||||
*
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
Iterable<String> getBasePackages();
|
||||
Streamable<String> getBasePackages();
|
||||
|
||||
/**
|
||||
* Returns the {@link QueryLookupStrategy.Key} to define how query methods shall be resolved.
|
||||
@@ -89,7 +89,7 @@ public interface RepositoryConfigurationSource {
|
||||
* @param loader
|
||||
* @return
|
||||
*/
|
||||
Collection<BeanDefinition> getCandidates(ResourceLoader loader);
|
||||
Streamable<BeanDefinition> getCandidates(ResourceLoader loader);
|
||||
|
||||
/**
|
||||
* Returns the value for the {@link String} attribute with the given name. The name is expected to be handed in
|
||||
@@ -115,12 +115,12 @@ public interface RepositoryConfigurationSource {
|
||||
*
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
Iterable<TypeFilter> getExcludeFilters();
|
||||
Streamable<TypeFilter> getExcludeFilters();
|
||||
|
||||
/**
|
||||
* Returns a name for the beanDefinition.
|
||||
*
|
||||
* @param beanDefinition Must not be {@literal null}.
|
||||
* @param beanDefinition must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
|
||||
@@ -15,15 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -58,27 +56,20 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfiguration#getCandidates(org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getCandidates(org.springframework.core.io.ResourceLoader)
|
||||
*/
|
||||
public Collection<BeanDefinition> getCandidates(ResourceLoader loader) {
|
||||
@Override
|
||||
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
|
||||
|
||||
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters());
|
||||
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
|
||||
scanner.setResourceLoader(loader);
|
||||
scanner.setEnvironment(environment);
|
||||
|
||||
for (TypeFilter filter : getExcludeFilters()) {
|
||||
scanner.addExcludeFilter(filter);
|
||||
}
|
||||
getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));
|
||||
|
||||
Set<BeanDefinition> result = new HashSet<>();
|
||||
|
||||
for (String basePackage : getBasePackages()) {
|
||||
Set<BeanDefinition> candidate = scanner.findCandidateComponents(basePackage);
|
||||
result.addAll(candidate);
|
||||
}
|
||||
|
||||
return result;
|
||||
return Streamable.of(() -> getBasePackages().stream()//
|
||||
.flatMap(it -> scanner.findCandidateComponents(it).stream()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,8 +78,18 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository
|
||||
*
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
public Iterable<TypeFilter> getExcludeFilters() {
|
||||
return Collections.emptySet();
|
||||
@Override
|
||||
public Streamable<TypeFilter> getExcludeFilters() {
|
||||
return Streamable.empty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBeanNameGenerator()
|
||||
*/
|
||||
@Override
|
||||
public String generateBeanName(BeanDefinition beanDefinition) {
|
||||
return beanNameGenerator.generateBeanName(beanDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,13 +111,4 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository
|
||||
public boolean shouldConsiderNestedRepositories() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBeanNameGenerator()
|
||||
*/
|
||||
@Override
|
||||
public String generateBeanName(BeanDefinition beanDefinition) {
|
||||
return beanNameGenerator.generateBeanName(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -26,41 +28,35 @@ import java.util.stream.Collectors;
|
||||
* filters are ignored.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
class SelectionSet<T> {
|
||||
|
||||
private final Collection<T> collection;
|
||||
private final Function<Collection<T>, T> fallback;
|
||||
private final Function<Collection<T>, Optional<T>> fallback;
|
||||
|
||||
/**
|
||||
* creates a {@link SelectionSet} with a default fallback of {@literal null}, when no element is found and an
|
||||
* {@link IllegalStateException} when no element is found.
|
||||
*/
|
||||
SelectionSet(Collection<T> collection) {
|
||||
this(collection, defaultFallback());
|
||||
static <T> SelectionSet<T> of(Collection<T> collection) {
|
||||
return new SelectionSet<>(collection, defaultFallback());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collection The collection from which a unique element will get picked.
|
||||
* @param fallback Will get called once {@literal #uniqueResult} gets called if no unique result can be determined.
|
||||
*/
|
||||
SelectionSet(Collection<T> collection, Function<Collection<T>, T> fallback) {
|
||||
|
||||
this.collection = collection;
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this <code>SelectionSet</code> contains exactly one element it gets returned. If no unique result can
|
||||
* be identified the fallback function passed in at the constructor gets called and its return value becomes
|
||||
* the return value of this method.
|
||||
* If this {@code SelectionSet} contains exactly one element it gets returned. If no unique result can be identified
|
||||
* the fallback function passed in at the constructor gets called and its return value becomes the return value of
|
||||
* this method.
|
||||
*
|
||||
* @return a unique result, or the result of the callback provided in the constructor.
|
||||
*/
|
||||
T uniqueResult() {
|
||||
T uniqueResult = findUniqueResult();
|
||||
Optional<T> uniqueResult() {
|
||||
|
||||
return uniqueResult != null ? uniqueResult : fallback.apply(collection);
|
||||
Optional<T> uniqueResult = findUniqueResult();
|
||||
|
||||
return uniqueResult.isPresent() ? uniqueResult : fallback.apply(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,26 +66,22 @@ class SelectionSet<T> {
|
||||
*/
|
||||
SelectionSet<T> filterIfNecessary(Predicate<T> predicate) {
|
||||
|
||||
if (findUniqueResult() != null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
List<T> fillteredList = collection.stream().filter(predicate).collect(Collectors.toList());
|
||||
return new SelectionSet<T>(fillteredList, fallback);
|
||||
return findUniqueResult().map(it -> this).orElseGet(
|
||||
() -> new SelectionSet<T>(collection.stream().filter(predicate).collect(Collectors.toList()), fallback));
|
||||
}
|
||||
|
||||
private static <S> Function<Collection<S>, S> defaultFallback() {
|
||||
private static <S> Function<Collection<S>, Optional<S>> defaultFallback() {
|
||||
|
||||
return c -> {
|
||||
if (c.isEmpty()) {
|
||||
return null;
|
||||
return Optional.empty();
|
||||
} else {
|
||||
throw new IllegalStateException("More then one element in collection.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private T findUniqueResult() {
|
||||
return collection.size() == 1 ? collection.iterator().next() : null;
|
||||
private Optional<T> findUniqueResult() {
|
||||
return Optional.ofNullable(collection.size() == 1 ? collection.iterator().next() : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -26,6 +25,7 @@ import org.springframework.data.config.TypeFilterParser;
|
||||
import org.springframework.data.config.TypeFilterParser.Type;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.util.ParsingUtils;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -89,10 +89,11 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBasePackages()
|
||||
*/
|
||||
public Iterable<String> getBasePackages() {
|
||||
public Streamable<String> getBasePackages() {
|
||||
|
||||
String attribute = element.getAttribute(BASE_PACKAGE);
|
||||
return Arrays.asList(StringUtils.delimitedListToStringArray(attribute, ",", " "));
|
||||
|
||||
return Streamable.of(StringUtils.delimitedListToStringArray(attribute, ",", " "));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,8 +126,8 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getExcludeFilters()
|
||||
*/
|
||||
@Override
|
||||
public Iterable<TypeFilter> getExcludeFilters() {
|
||||
return excludeFilters;
|
||||
public Streamable<TypeFilter> getExcludeFilters() {
|
||||
return Streamable.of(excludeFilters);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user