Ordered stream access on ObjectProvider with strong order guarantees
Issue: SPR-17272
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user