Skip to content

Commit ad949a7

Browse files
committed
Add includeNonSingletons flag for ObjectProvider stream access
Closes gh-34591
1 parent 86b2617 commit ad949a7

File tree

4 files changed

+101
-25
lines changed

4 files changed

+101
-25
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java

+39-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ default Stream<T> orderedStream() {
274274
* @see #orderedStream(Predicate)
275275
*/
276276
default Stream<T> stream(Predicate<Class<?>> customFilter) {
277-
return stream().filter(obj -> customFilter.test(obj.getClass()));
277+
return stream(customFilter, true);
278278
}
279279

280280
/**
@@ -288,6 +288,44 @@ default Stream<T> stream(Predicate<Class<?>> customFilter) {
288288
* @see #stream(Predicate)
289289
*/
290290
default Stream<T> orderedStream(Predicate<Class<?>> customFilter) {
291+
return orderedStream(customFilter, true);
292+
}
293+
294+
/**
295+
* Return a custom-filtered {@link Stream} over all matching object instances,
296+
* without specific ordering guarantees (but typically in registration order).
297+
* @param customFilter a custom type filter for selecting beans among the raw
298+
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
299+
* any default filtering)
300+
* @param includeNonSingletons whether to include prototype or scoped beans too
301+
* or just singletons (also applies to FactoryBeans)
302+
* @since 6.2.5
303+
* @see #stream(Predicate)
304+
* @see #orderedStream(Predicate, boolean)
305+
*/
306+
default Stream<T> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
307+
if (!includeNonSingletons) {
308+
throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default");
309+
}
310+
return stream().filter(obj -> customFilter.test(obj.getClass()));
311+
}
312+
313+
/**
314+
* Return a custom-filtered {@link Stream} over all matching object instances,
315+
* pre-ordered according to the factory's common order comparator.
316+
* @param customFilter a custom type filter for selecting beans among the raw
317+
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
318+
* any default filtering)
319+
* @param includeNonSingletons whether to include prototype or scoped beans too
320+
* or just singletons (also applies to FactoryBeans)
321+
* @since 6.2.5
322+
* @see #orderedStream()
323+
* @see #stream(Predicate)
324+
*/
325+
default Stream<T> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
326+
if (!includeNonSingletons) {
327+
throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default");
328+
}
291329
return orderedStream().filter(obj -> customFilter.test(obj.getClass()));
292330
}
293331

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -487,14 +487,14 @@ public void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
487487
@SuppressWarnings("unchecked")
488488
@Override
489489
public Stream<T> stream() {
490-
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
490+
return Arrays.stream(beanNamesForStream(requiredType, true, allowEagerInit))
491491
.map(name -> (T) getBean(name))
492492
.filter(bean -> !(bean instanceof NullBean));
493493
}
494494
@SuppressWarnings("unchecked")
495495
@Override
496496
public Stream<T> orderedStream() {
497-
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
497+
String[] beanNames = beanNamesForStream(requiredType, true, allowEagerInit);
498498
if (beanNames.length == 0) {
499499
return Stream.empty();
500500
}
@@ -510,16 +510,16 @@ public Stream<T> orderedStream() {
510510
}
511511
@SuppressWarnings("unchecked")
512512
@Override
513-
public Stream<T> stream(Predicate<Class<?>> customFilter) {
514-
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
513+
public Stream<T> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
514+
return Arrays.stream(beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit))
515515
.filter(name -> customFilter.test(getType(name)))
516516
.map(name -> (T) getBean(name))
517517
.filter(bean -> !(bean instanceof NullBean));
518518
}
519519
@SuppressWarnings("unchecked")
520520
@Override
521-
public Stream<T> orderedStream(Predicate<Class<?>> customFilter) {
522-
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
521+
public Stream<T> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
522+
String[] beanNames = beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit);
523523
if (beanNames.length == 0) {
524524
return Stream.empty();
525525
}
@@ -559,8 +559,8 @@ else if (parent != null) {
559559
return null;
560560
}
561561

562-
private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) {
563-
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit);
562+
private String[] beanNamesForStream(ResolvableType requiredType, boolean includeNonSingletons, boolean allowEagerInit) {
563+
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, includeNonSingletons, allowEagerInit);
564564
}
565565

566566
@Override
@@ -2544,17 +2544,17 @@ private Stream<Object> resolveStream(boolean ordered) {
25442544
}
25452545

25462546
@Override
2547-
public Stream<Object> stream(Predicate<Class<?>> customFilter) {
2548-
return Arrays.stream(getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true))
2547+
public Stream<Object> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
2548+
return Arrays.stream(beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true))
25492549
.filter(name -> AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, name))
25502550
.filter(name -> customFilter.test(getType(name)))
25512551
.map(name -> getBean(name))
25522552
.filter(bean -> !(bean instanceof NullBean));
25532553
}
25542554

25552555
@Override
2556-
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter) {
2557-
String[] beanNames = getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true);
2556+
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
2557+
String[] beanNames = beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true);
25582558
if (beanNames.length == 0) {
25592559
return Stream.empty();
25602560
}

spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

+16-10
Original file line numberDiff line numberDiff line change
@@ -1519,34 +1519,34 @@ void orderFromAttribute() {
15191519
bd2.setBeanClass(DerivedTestBean.class);
15201520
bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest"))));
15211521
bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
1522+
bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
15221523
lbf.registerBeanDefinition("bean2", bd2);
15231524
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
15241525
.containsExactly("highest", "lowest");
1525-
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
1526-
.containsExactly("highest", "lowest");
15271526
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz))
15281527
.map(TestBean::getName)).containsExactly("lowest");
1528+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
1529+
.containsExactly("highest", "lowest");
1530+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName))
1531+
.containsExactly("lowest");
15291532
}
15301533

15311534
@Test
1532-
void orderFromAttributeOverrideAnnotation() {
1535+
void orderFromAttributeOverridesAnnotation() {
15331536
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
15341537
RootBeanDefinition rbd1 = new RootBeanDefinition(LowestPrecedenceTestBeanFactoryBean.class);
15351538
rbd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
15361539
lbf.registerBeanDefinition("lowestPrecedenceFactory", rbd1);
15371540
RootBeanDefinition rbd2 = new RootBeanDefinition(HighestPrecedenceTestBeanFactoryBean.class);
15381541
rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
1542+
rbd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
15391543
lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2);
1540-
GenericBeanDefinition bd1 = new GenericBeanDefinition();
1541-
bd1.setFactoryBeanName("highestPrecedenceFactory");
1542-
lbf.registerBeanDefinition("bean1", bd1);
1543-
GenericBeanDefinition bd2 = new GenericBeanDefinition();
1544-
bd2.setFactoryBeanName("lowestPrecedenceFactory");
1545-
lbf.registerBeanDefinition("bean2", bd2);
15461544
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
15471545
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
15481546
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
15491547
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
1548+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName))
1549+
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean");
15501550
}
15511551

15521552
@Test
@@ -1987,7 +1987,6 @@ void getBeanByTypeInstanceDefinedInParent() {
19871987
void getBeanByTypeInstanceWithAmbiguity() {
19881988
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
19891989
RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class);
1990-
bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
19911990
bd2.getConstructorArgumentValues().addGenericArgumentValue("43");
19921991
lbf.registerBeanDefinition("bd1", bd1);
19931992
lbf.registerBeanDefinition("bd2", bd2);
@@ -2028,6 +2027,10 @@ void getBeanByTypeInstanceWithAmbiguity() {
20282027
assertThat(resolved).hasSize(2);
20292028
assertThat(resolved).contains(lbf.getBean("bd1"));
20302029
assertThat(resolved).contains(lbf.getBean("bd2"));
2030+
2031+
resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet());
2032+
assertThat(resolved).hasSize(1);
2033+
assertThat(resolved).contains(lbf.getBean("bd2"));
20312034
}
20322035

20332036
@Test
@@ -2082,6 +2085,9 @@ void getBeanByTypeInstanceWithPrimary() {
20822085
assertThat(resolved).hasSize(2);
20832086
assertThat(resolved).contains(lbf.getBean("bd1"));
20842087
assertThat(resolved).contains(lbf.getBean("bd2"));
2088+
2089+
resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet());
2090+
assertThat(resolved).isEmpty();
20852091
}
20862092

20872093
@Test

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,10 @@ void objectProviderInjectionWithPrototype() {
16141614
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
16151615
testBeans = bean.allTestBeansInOrder();
16161616
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
1617+
testBeans = bean.allSingletonBeans();
1618+
assertThat(testBeans).isEmpty();
1619+
testBeans = bean.allSingletonBeansInOrder();
1620+
assertThat(testBeans).isEmpty();
16171621
}
16181622

16191623
@Test
@@ -1648,6 +1652,12 @@ void objectProviderInjectionWithSingletonTarget() {
16481652
testBeans = bean.allTestBeansInOrder();
16491653
assertThat(testBeans).hasSize(1);
16501654
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
1655+
testBeans = bean.allSingletonBeans();
1656+
assertThat(testBeans).hasSize(1);
1657+
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
1658+
testBeans = bean.allSingletonBeansInOrder();
1659+
assertThat(testBeans).hasSize(1);
1660+
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
16511661
}
16521662

16531663
@Test
@@ -1675,6 +1685,10 @@ void objectProviderInjectionWithTargetNotAvailable() {
16751685
assertThat(testBeans).isEmpty();
16761686
testBeans = bean.allTestBeansInOrder();
16771687
assertThat(testBeans).isEmpty();
1688+
testBeans = bean.allSingletonBeans();
1689+
assertThat(testBeans).isEmpty();
1690+
testBeans = bean.allSingletonBeansInOrder();
1691+
assertThat(testBeans).isEmpty();
16781692
}
16791693

16801694
@Test
@@ -1698,6 +1712,8 @@ void objectProviderInjectionWithTargetNotUnique() {
16981712
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean1, testBean2);
16991713
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
17001714
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean1, testBean2);
1715+
assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2);
1716+
assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean1, testBean2);
17011717
}
17021718

17031719
@Test
@@ -1728,6 +1744,8 @@ void objectProviderInjectionWithTargetPrimary() {
17281744
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean2, testBean1);
17291745
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
17301746
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean2, testBean1);
1747+
assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2);
1748+
assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean2, testBean1);
17311749
}
17321750

17331751
@Test
@@ -1739,14 +1757,15 @@ void objectProviderInjectionWithUnresolvedOrderedStream() {
17391757
bf.registerBeanDefinition("testBean1", tb1);
17401758
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
17411759
tb2.setFactoryMethodName("newTestBean2");
1742-
tb2.setLazyInit(true);
1760+
tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
17431761
bf.registerBeanDefinition("testBean2", tb2);
17441762

17451763
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
17461764
assertThat(bean.streamTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
17471765
bf.getBean("testBean1", TestBean.class));
17481766
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
17491767
bf.getBean("testBean1", TestBean.class));
1768+
assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class));
17501769
}
17511770

17521771
@Test
@@ -1757,6 +1776,7 @@ void objectProviderInjectionWithNonCandidatesInStream() {
17571776
bf.registerBeanDefinition("testBean1", tb1);
17581777
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
17591778
tb2.setFactoryMethodName("newTestBean2");
1779+
tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
17601780
bf.registerBeanDefinition("testBean2", tb2);
17611781

17621782
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
@@ -1789,6 +1809,10 @@ void objectProviderInjectionWithNonCandidatesInStream() {
17891809
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean4", TestBean.class));
17901810
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
17911811
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean4", TestBean.class));
1812+
assertThat(bean.allSingletonBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1813+
bf.getBean("testBean4", TestBean.class));
1814+
assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class),
1815+
bf.getBean("testBean4", TestBean.class));
17921816

17931817
Map<String, TestBean> typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class);
17941818
assertThat(typeMatches.remove("testBean3")).isNotNull();
@@ -2370,7 +2394,7 @@ void genericsBasedConstructorInjection() {
23702394
}
23712395

23722396
@Test
2373-
@SuppressWarnings({ "rawtypes", "unchecked" })
2397+
@SuppressWarnings("unchecked")
23742398
void genericsBasedConstructorInjectionWithNonTypedTarget() {
23752399
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
23762400
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -3393,6 +3417,14 @@ public List<TestBean> allTestBeans() {
33933417
public List<TestBean> allTestBeansInOrder() {
33943418
return this.testBean.orderedStream(ObjectProvider.UNFILTERED).toList();
33953419
}
3420+
3421+
public List<TestBean> allSingletonBeans() {
3422+
return this.testBean.stream(ObjectProvider.UNFILTERED, false).toList();
3423+
}
3424+
3425+
public List<TestBean> allSingletonBeansInOrder() {
3426+
return this.testBean.orderedStream(ObjectProvider.UNFILTERED, false).toList();
3427+
}
33963428
}
33973429

33983430

0 commit comments

Comments
 (0)