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);
}
};
}

View File

@ -1289,15 +1289,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
@Test
public void testObjectProviderInjectionWithTargetPrimary() {
bf.setDependencyComparator(Comparators.comparable());
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
RootBeanDefinition tb1 = new RootBeanDefinition(TestBean.class);
tb1.getPropertyValues().add("name", "yours");
RootBeanDefinition tb1 = new RootBeanDefinition(OrderedTestBean.class);
tb1.getPropertyValues().add("order", 1);
tb1.setPrimary(true);
bf.registerBeanDefinition("testBean1", tb1);
RootBeanDefinition tb2 = new RootBeanDefinition(TestBean.class);
tb2.getPropertyValues().add("name", "mine");
RootBeanDefinition tb2 = new RootBeanDefinition(OrderedTestBean.class);
tb2.getPropertyValues().add("order", 0);
tb2.setLazyInit(true);
bf.registerBeanDefinition("testBean2", tb2);
@ -2885,7 +2883,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public List<TestBean> sortedTestBeans() {
return this.testBeanProvider.toList();
return this.testBeanProvider.orderedStream().collect(Collectors.toList());
}
}
@ -2983,6 +2981,21 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public static class OrderedTestBean extends TestBean implements Ordered {
private int order;
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
public static class OrderedNestedTestBean extends NestedTestBean implements Ordered {
private int order;

View File

@ -43,6 +43,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.core.Ordered;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.ClassPathResource;
@ -53,7 +54,6 @@ import org.springframework.tests.sample.beans.GenericBean;
import org.springframework.tests.sample.beans.GenericIntegerBean;
import org.springframework.tests.sample.beans.GenericSetOfIntegerBean;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.comparator.Comparators;
import static org.junit.Assert.*;
@ -847,7 +847,6 @@ public class BeanFactoryGenericsTests {
public void testGenericMatchingWithFullTypeDifferentiation() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
bf.setDependencyComparator(Comparators.comparable());
bf.registerBeanDefinition("store1", new RootBeanDefinition(DoubleStore.class));
bf.registerBeanDefinition("store2", new RootBeanDefinition(FloatStore.class));
@ -907,7 +906,7 @@ public class BeanFactoryGenericsTests {
assertSame(bf.getBean("store1"), resolved.get(0));
assertSame(bf.getBean("store2"), resolved.get(1));
resolved = numberStoreProvider.toList();
resolved = numberStoreProvider.orderedStream().collect(Collectors.toList());
assertEquals(2, resolved.size());
assertSame(bf.getBean("store2"), resolved.get(0));
assertSame(bf.getBean("store1"), resolved.get(1));
@ -923,7 +922,7 @@ public class BeanFactoryGenericsTests {
assertEquals(1, resolved.size());
assertTrue(resolved.contains(bf.getBean("store1")));
resolved = (List) doubleStoreProvider.toList();
resolved = (List) doubleStoreProvider.orderedStream().collect(Collectors.toList());
assertEquals(1, resolved.size());
assertTrue(resolved.contains(bf.getBean("store1")));
@ -938,7 +937,7 @@ public class BeanFactoryGenericsTests {
assertEquals(1, resolved.size());
assertTrue(resolved.contains(bf.getBean("store2")));
resolved = (List) floatStoreProvider.toList();
resolved = (List) floatStoreProvider.orderedStream().collect(Collectors.toList());
assertEquals(1, resolved.size());
assertTrue(resolved.contains(bf.getBean("store2")));
}
@ -1006,20 +1005,25 @@ public class BeanFactoryGenericsTests {
}
public static class NumberStore<T extends Number> implements Comparable<NumberStore> {
public static class NumberStore<T extends Number> {
}
public static class DoubleStore extends NumberStore<Double> implements Ordered {
@Override
public int compareTo(NumberStore other) {
return getClass().getName().compareTo(other.getClass().getName()) * -1;
public int getOrder() {
return 1;
}
}
public static class DoubleStore extends NumberStore<Double> {
}
public static class FloatStore extends NumberStore<Float> implements Ordered {
public static class FloatStore extends NumberStore<Float> {
@Override
public int getOrder() {
return 0;
}
}

View File

@ -59,7 +59,7 @@ public class OrderComparator implements Comparator<Object> {
* @return the adapted comparator
* @since 4.1
*/
public Comparator<Object> withSourceProvider(final OrderSourceProvider sourceProvider) {
public Comparator<Object> withSourceProvider(OrderSourceProvider sourceProvider) {
return (o1, o2) -> doCompare(o1, o2, sourceProvider);
}
@ -78,10 +78,9 @@ public class OrderComparator implements Comparator<Object> {
return 1;
}
// Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
return Integer.compare(i1, i2);
}
/**