Ordered stream access on ObjectProvider with strong order guarantees

Issue: SPR-17272
This commit is contained in:
Juergen Hoeller
2018-09-14 23:56:25 +02:00
parent 12240c7524
commit 41d4cb5cbf
6 changed files with 92 additions and 71 deletions

View File

@@ -17,10 +17,8 @@
package org.springframework.beans.factory;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.BeansException;
@@ -141,17 +139,7 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
}
/**
* Return a sequential {@link Stream} over lazily resolved object instances,
* without specific ordering guarantees (but typically in registration order).
* @since 5.1
* @see #iterator()
*/
default Stream<T> stream() {
throw new UnsupportedOperationException("Multi-element access not supported");
}
/**
* Return an {@link Iterator} over lazily resolved object instances,
* Return an {@link Iterator} over all matching object instances,
* without specific ordering guarantees (but typically in registration order).
* @since 5.1
* @see #stream()
@@ -162,17 +150,30 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
}
/**
* Return a {@link List} with fully resolved object instances,
* potentially pre-ordered according to a common comparator.
* <p>In a common Spring application context, this will be ordered
* according to {@link org.springframework.core.Ordered} /
* {@link org.springframework.core.annotation.Order} conventions,
* Return a sequential {@link Stream} over all matching object instances,
* without specific ordering guarantees (but typically in registration order).
* @since 5.1
* @see #iterator()
* @see #orderedStream()
*/
default Stream<T> stream() {
throw new UnsupportedOperationException("Multi-element access not supported");
}
/**
* Return a sequential {@link Stream} over all matching object instances,
* pre-ordered according to the factory's common order comparator.
* <p>In a standard Spring application context, this will be ordered
* according to {@link org.springframework.core.Ordered} conventions,
* and in case of annotation-based configuration also considering the
* {@link org.springframework.core.annotation.Order} annotation,
* analogous to multi-element injection points of list/array type.
* @since 5.1
* @see #stream()
* @see org.springframework.core.OrderComparator
*/
default List<T> toList() {
return stream().collect(Collectors.toList());
default Stream<T> orderedStream() {
throw new UnsupportedOperationException("Multi-element access not supported");
}
}

View File

@@ -40,7 +40,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Provider;
@@ -388,7 +387,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
.filter(bean -> !(bean instanceof NullBean));
}
@Override
public List<T> toList() {
public Stream<T> orderedStream() {
String[] beanNames = getBeanNamesForType(requiredType);
Map<String, T> matchingBeans = new LinkedHashMap<>(beanNames.length);
for (String beanName : beanNames) {
@@ -397,12 +396,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
matchingBeans.put(beanName, (T) beanInstance);
}
}
List<T> result = new ArrayList<>(matchingBeans.values());
Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
if (comparator != null) {
result.sort(comparator);
}
return result;
Stream<T> stream = matchingBeans.values().stream();
return stream.sorted(adaptOrderComparator(matchingBeans));
}
};
}
@@ -1267,16 +1262,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
Stream<Object> result = matchingBeans.keySet().stream()
Stream<Object> stream = matchingBeans.keySet().stream()
.map(name -> descriptor.resolveCandidate(name, type, this))
.filter(bean -> !(bean instanceof NullBean));
if (((StreamDependencyDescriptor) descriptor).isSorted()) {
Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
if (comparator != null) {
result = result.sorted(comparator);
}
if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
stream = stream.sorted(adaptOrderComparator(matchingBeans));
}
return result;
return stream;
}
else if (type.isArray()) {
Class<?> componentType = type.getComponentType();
@@ -1375,6 +1367,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
private Comparator<Object> adaptOrderComparator(Map<String, ?> matchingBeans) {
Comparator<Object> dependencyComparator = getDependencyComparator();
OrderComparator comparator = (dependencyComparator instanceof OrderComparator ?
(OrderComparator) dependencyComparator : OrderComparator.INSTANCE);
return comparator.withSourceProvider(createFactoryAwareOrderSourceProvider(matchingBeans));
}
private OrderComparator.OrderSourceProvider createFactoryAwareOrderSourceProvider(Map<String, ?> beans) {
IdentityHashMap<Object, String> instancesToBeanNames = new IdentityHashMap<>();
beans.forEach((beanName, instance) -> instancesToBeanNames.put(instance, beanName));
@@ -1779,15 +1778,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
*/
private static class StreamDependencyDescriptor extends DependencyDescriptor {
private final boolean sorted;
private final boolean ordered;
public StreamDependencyDescriptor(DependencyDescriptor original, boolean sorted) {
public StreamDependencyDescriptor(DependencyDescriptor original, boolean ordered) {
super(original);
this.sorted = sorted;
this.ordered = ordered;
}
public boolean isSorted() {
return this.sorted;
public boolean isOrdered() {
return this.ordered;
}
}
@@ -1897,22 +1896,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
@SuppressWarnings("unchecked")
@Override
public Stream<Object> stream() {
DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, false);
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
Assert.state(result instanceof Stream, "Stream expected");
return (Stream<Object>) result;
return resolveStream(false);
}
@Override
public Stream<Object> orderedStream() {
return resolveStream(true);
}
@SuppressWarnings("unchecked")
@Override
public List<Object> toList() {
DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, true);
private Stream<Object> resolveStream(boolean ordered) {
DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, ordered);
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
Assert.state(result instanceof Stream, "Stream expected");
return ((Stream<Object>) result).collect(Collectors.toList());
return (Stream<Object>) result;
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.core.OrderComparator;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
@@ -245,6 +246,10 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public Stream<T> stream() {
return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name));
}
@Override
public Stream<T> orderedStream() {
return stream().sorted(OrderComparator.INSTANCE);
}
};
}