Skip to content

Commit 088d53a

Browse files
committed
Merge branch '6.2.x'
2 parents ecc7ddd + 1b18928 commit 088d53a

File tree

5 files changed

+216
-16
lines changed

5 files changed

+216
-16
lines changed

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

+47-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.util.Iterator;
2020
import java.util.function.Consumer;
21+
import java.util.function.Predicate;
2122
import java.util.function.Supplier;
2223
import java.util.stream.Stream;
2324

@@ -54,6 +55,15 @@
5455
*/
5556
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
5657

58+
/**
59+
* A predicate for unfiltered type matches.
60+
* @since 6.2.3
61+
* @see #stream(Predicate)
62+
* @see #orderedStream(Predicate)
63+
*/
64+
Predicate<Class<?>> UNFILTERED = (clazz -> true);
65+
66+
5767
@Override
5868
default T getObject() throws BeansException {
5969
Iterator<T> it = iterator();
@@ -197,6 +207,10 @@ default Iterator<T> iterator() {
197207
/**
198208
* Return a sequential {@link Stream} over all matching object instances,
199209
* without specific ordering guarantees (but typically in registration order).
210+
* <p>Note: The result may be filtered by default according to qualifiers on the
211+
* injection point versus target beans and the general autowire candidate status
212+
* of matching beans. For custom filtering against the raw type matches, use
213+
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
200214
* @since 5.1
201215
* @see #iterator()
202216
* @see #orderedStream()
@@ -218,6 +232,10 @@ default Stream<T> stream() {
218232
* {@link #stream()} method. You may override this to apply an
219233
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator}
220234
* if necessary.
235+
* <p>Note: The result may be filtered by default according to qualifiers on the
236+
* injection point versus target beans and the general autowire candidate status
237+
* of matching beans. For custom filtering against the raw type matches, use
238+
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
221239
* @since 5.1
222240
* @see #stream()
223241
* @see org.springframework.core.OrderComparator
@@ -226,4 +244,32 @@ default Stream<T> orderedStream() {
226244
return stream().sorted(OrderComparator.INSTANCE);
227245
}
228246

247+
/**
248+
* Return a custom-filtered {@link Stream} over all matching object instances,
249+
* without specific ordering guarantees (but typically in registration order).
250+
* @param customFilter a custom type filter for selecting beans among the raw
251+
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
252+
* any default filtering)
253+
* @since 6.2.3
254+
* @see #stream()
255+
* @see #orderedStream(Predicate)
256+
*/
257+
default Stream<T> stream(Predicate<Class<?>> customFilter) {
258+
return stream().filter(obj -> customFilter.test(obj.getClass()));
259+
}
260+
261+
/**
262+
* Return a custom-filtered {@link Stream} over all matching object instances,
263+
* pre-ordered according to the factory's common order comparator.
264+
* @param customFilter a custom type filter for selecting beans among the raw
265+
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
266+
* any default filtering)
267+
* @since 6.2.3
268+
* @see #orderedStream()
269+
* @see #stream(Predicate)
270+
*/
271+
default Stream<T> orderedStream(Predicate<Class<?>> customFilter) {
272+
return orderedStream().filter(obj -> customFilter.test(obj.getClass()));
273+
}
274+
229275
}

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

+54-2
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,32 @@ public Stream<T> orderedStream() {
497497
Stream<T> stream = matchingBeans.values().stream();
498498
return stream.sorted(adaptOrderComparator(matchingBeans));
499499
}
500+
@SuppressWarnings("unchecked")
501+
@Override
502+
public Stream<T> stream(Predicate<Class<?>> customFilter) {
503+
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
504+
.filter(name -> customFilter.test(getType(name)))
505+
.map(name -> (T) getBean(name))
506+
.filter(bean -> !(bean instanceof NullBean));
507+
}
508+
@SuppressWarnings("unchecked")
509+
@Override
510+
public Stream<T> orderedStream(Predicate<Class<?>> customFilter) {
511+
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
512+
if (beanNames.length == 0) {
513+
return Stream.empty();
514+
}
515+
Map<String, T> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
516+
for (String beanName : beanNames) {
517+
if (customFilter.test(getType(beanName))) {
518+
Object beanInstance = getBean(beanName);
519+
if (!(beanInstance instanceof NullBean)) {
520+
matchingBeans.put(beanName, (T) beanInstance);
521+
}
522+
}
523+
}
524+
return matchingBeans.values().stream().sorted(adaptOrderComparator(matchingBeans));
525+
}
500526
};
501527
}
502528

@@ -1865,8 +1891,8 @@ private void addCandidateEntry(Map<String, Object> candidates, String candidateN
18651891
candidates.put(candidateName, beanInstance);
18661892
}
18671893
}
1868-
else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor streamDescriptor &&
1869-
streamDescriptor.isOrdered())) {
1894+
else if (containsSingleton(candidateName) ||
1895+
(descriptor instanceof StreamDependencyDescriptor streamDescriptor && streamDescriptor.isOrdered())) {
18701896
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
18711897
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
18721898
}
@@ -2450,6 +2476,32 @@ private Stream<Object> resolveStream(boolean ordered) {
24502476
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
24512477
return (result instanceof Stream stream ? stream : Stream.of(result));
24522478
}
2479+
2480+
@Override
2481+
public Stream<Object> stream(Predicate<Class<?>> customFilter) {
2482+
return Arrays.stream(getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true))
2483+
.filter(name -> customFilter.test(getType(name)))
2484+
.map(name -> getBean(name))
2485+
.filter(bean -> !(bean instanceof NullBean));
2486+
}
2487+
2488+
@Override
2489+
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter) {
2490+
String[] beanNames = getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true);
2491+
if (beanNames.length == 0) {
2492+
return Stream.empty();
2493+
}
2494+
Map<String, Object> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
2495+
for (String beanName : beanNames) {
2496+
if (customFilter.test(getType(beanName))) {
2497+
Object beanInstance = getBean(beanName);
2498+
if (!(beanInstance instanceof NullBean)) {
2499+
matchingBeans.put(beanName, beanInstance);
2500+
}
2501+
}
2502+
}
2503+
return matchingBeans.values().stream().sorted(adaptOrderComparator(matchingBeans));
2504+
}
24532505
}
24542506

24552507

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

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1515,12 +1515,16 @@ void orderFromAttribute() {
15151515
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
15161516
lbf.registerBeanDefinition("bean1", bd1);
15171517
GenericBeanDefinition bd2 = new GenericBeanDefinition();
1518-
bd2.setBeanClass(TestBean.class);
1518+
bd2.setBeanClass(DerivedTestBean.class);
15191519
bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest"))));
15201520
bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
15211521
lbf.registerBeanDefinition("bean2", bd2);
15221522
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
15231523
.containsExactly("highest", "lowest");
1524+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
1525+
.containsExactly("highest", "lowest");
1526+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz))
1527+
.map(TestBean::getName)).containsExactly("lowest");
15241528
}
15251529

15261530
@Test
@@ -1540,6 +1544,8 @@ void orderFromAttributeOverrideAnnotation() {
15401544
lbf.registerBeanDefinition("bean2", bd2);
15411545
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
15421546
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
1547+
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
1548+
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
15431549
}
15441550

15451551
@Test
@@ -1934,6 +1940,11 @@ void getBeanByTypeInstanceWithAmbiguity() {
19341940
assertThat(resolved).hasSize(2);
19351941
assertThat(resolved).contains(lbf.getBean("bd1"));
19361942
assertThat(resolved).contains(lbf.getBean("bd2"));
1943+
1944+
resolved = provider.stream(ObjectProvider.UNFILTERED).collect(Collectors.toSet());
1945+
assertThat(resolved).hasSize(2);
1946+
assertThat(resolved).contains(lbf.getBean("bd1"));
1947+
assertThat(resolved).contains(lbf.getBean("bd2"));
19371948
}
19381949

19391950
@Test
@@ -1983,6 +1994,11 @@ void getBeanByTypeInstanceWithPrimary() {
19831994
assertThat(resolved).hasSize(2);
19841995
assertThat(resolved).contains(lbf.getBean("bd1"));
19851996
assertThat(resolved).contains(lbf.getBean("bd2"));
1997+
1998+
resolved = provider.stream(ObjectProvider.UNFILTERED).collect(Collectors.toSet());
1999+
assertThat(resolved).hasSize(2);
2000+
assertThat(resolved).contains(lbf.getBean("bd1"));
2001+
assertThat(resolved).contains(lbf.getBean("bd2"));
19862002
}
19872003

19882004
@Test
@@ -2378,11 +2394,20 @@ void beanProviderWithParentBeanFactoryAndMixedOrder() {
23782394
parentBf.registerBeanDefinition("highPriorityTestBean", bd2);
23792395

23802396
ObjectProvider<TestBean> testBeanProvider = lbf.getBeanProvider(ResolvableType.forClass(TestBean.class));
2381-
List<TestBean> resolved = testBeanProvider.orderedStream().toList();
2382-
assertThat(resolved).containsExactly(
2397+
assertThat(testBeanProvider.orderedStream()).containsExactly(
23832398
lbf.getBean("highPriorityTestBean", TestBean.class),
23842399
lbf.getBean("lowPriorityTestBean", TestBean.class),
23852400
lbf.getBean("plainTestBean", TestBean.class));
2401+
assertThat(testBeanProvider.orderedStream(clazz -> clazz != TestBean.class).toList()).containsExactly(
2402+
lbf.getBean("highPriorityTestBean", TestBean.class),
2403+
lbf.getBean("lowPriorityTestBean", TestBean.class));
2404+
assertThat(testBeanProvider.stream()).containsExactly(
2405+
lbf.getBean("plainTestBean", TestBean.class),
2406+
lbf.getBean("lowPriorityTestBean", TestBean.class),
2407+
lbf.getBean("highPriorityTestBean", TestBean.class));
2408+
assertThat(testBeanProvider.orderedStream(clazz -> clazz != TestBean.class).toList()).containsExactly(
2409+
lbf.getBean("lowPriorityTestBean", TestBean.class),
2410+
lbf.getBean("highPriorityTestBean", TestBean.class));
23862411
}
23872412

23882413
@Test

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

+82-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -65,6 +65,7 @@
6565
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
6666
import org.springframework.beans.factory.support.GenericBeanDefinition;
6767
import org.springframework.beans.factory.support.RootBeanDefinition;
68+
import org.springframework.beans.testfixture.beans.DerivedTestBean;
6869
import org.springframework.beans.testfixture.beans.ITestBean;
6970
import org.springframework.beans.testfixture.beans.IndexedTestBean;
7071
import org.springframework.beans.testfixture.beans.NestedTestBean;
@@ -1605,7 +1606,11 @@ void objectProviderInjectionWithPrototype() {
16051606
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
16061607
testBeans = bean.streamTestBeans();
16071608
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
1608-
testBeans = bean.sortedTestBeans();
1609+
testBeans = bean.streamTestBeansInOrder();
1610+
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
1611+
testBeans = bean.allTestBeans();
1612+
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
1613+
testBeans = bean.allTestBeansInOrder();
16091614
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
16101615
}
16111616

@@ -1632,7 +1637,13 @@ void objectProviderInjectionWithSingletonTarget() {
16321637
testBeans = bean.streamTestBeans();
16331638
assertThat(testBeans).hasSize(1);
16341639
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
1635-
testBeans = bean.sortedTestBeans();
1640+
testBeans = bean.streamTestBeansInOrder();
1641+
assertThat(testBeans).hasSize(1);
1642+
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
1643+
testBeans = bean.allTestBeans();
1644+
assertThat(testBeans).hasSize(1);
1645+
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
1646+
testBeans = bean.allTestBeansInOrder();
16361647
assertThat(testBeans).hasSize(1);
16371648
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
16381649
}
@@ -1656,7 +1667,11 @@ void objectProviderInjectionWithTargetNotAvailable() {
16561667
assertThat(testBeans).isEmpty();
16571668
testBeans = bean.streamTestBeans();
16581669
assertThat(testBeans).isEmpty();
1659-
testBeans = bean.sortedTestBeans();
1670+
testBeans = bean.streamTestBeansInOrder();
1671+
assertThat(testBeans).isEmpty();
1672+
testBeans = bean.allTestBeans();
1673+
assertThat(testBeans).isEmpty();
1674+
testBeans = bean.allTestBeansInOrder();
16601675
assertThat(testBeans).isEmpty();
16611676
}
16621677

@@ -1678,7 +1693,9 @@ void objectProviderInjectionWithTargetNotUnique() {
16781693
assertThat(bean.iterateTestBeans()).containsExactly(testBean1, testBean2);
16791694
assertThat(bean.forEachTestBeans()).containsExactly(testBean1, testBean2);
16801695
assertThat(bean.streamTestBeans()).containsExactly(testBean1, testBean2);
1681-
assertThat(bean.sortedTestBeans()).containsExactly(testBean1, testBean2);
1696+
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean1, testBean2);
1697+
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
1698+
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean1, testBean2);
16821699
}
16831700

16841701
@Test
@@ -1706,7 +1723,9 @@ void objectProviderInjectionWithTargetPrimary() {
17061723
assertThat(bean.iterateTestBeans()).containsExactly(testBean1, testBean2);
17071724
assertThat(bean.forEachTestBeans()).containsExactly(testBean1, testBean2);
17081725
assertThat(bean.streamTestBeans()).containsExactly(testBean1, testBean2);
1709-
assertThat(bean.sortedTestBeans()).containsExactly(testBean2, testBean1);
1726+
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean2, testBean1);
1727+
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
1728+
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean2, testBean1);
17101729
}
17111730

17121731
@Test
@@ -1722,8 +1741,47 @@ void objectProviderInjectionWithUnresolvedOrderedStream() {
17221741
bf.registerBeanDefinition("testBean2", tb2);
17231742

17241743
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
1725-
assertThat(bean.sortedTestBeans()).containsExactly(bf.getBean("testBean2", TestBean.class),
1744+
assertThat(bean.streamTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1745+
bf.getBean("testBean1", TestBean.class));
1746+
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1747+
bf.getBean("testBean1", TestBean.class));
1748+
}
1749+
1750+
@Test
1751+
void objectProviderInjectionWithNonCandidatesInStream() {
1752+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
1753+
RootBeanDefinition tb1 = new RootBeanDefinition(TestBeanFactory.class);
1754+
tb1.setFactoryMethodName("newTestBean1");
1755+
bf.registerBeanDefinition("testBean1", tb1);
1756+
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
1757+
tb2.setFactoryMethodName("newTestBean2");
1758+
bf.registerBeanDefinition("testBean2", tb2);
1759+
RootBeanDefinition tb3 = new RootBeanDefinition(TestBean.class);
1760+
tb3.setAutowireCandidate(false);
1761+
tb3.setLazyInit(true);
1762+
bf.registerBeanDefinition("testBean3", tb3);
1763+
RootBeanDefinition tb4 = new RootBeanDefinition(DerivedTestBean.class);
1764+
tb4.setDefaultCandidate(false);
1765+
tb4.setLazyInit(true);
1766+
bf.registerBeanDefinition("testBean4", tb4);
1767+
1768+
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
1769+
assertThat(bean.streamTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1770+
bf.getBean("testBean2", TestBean.class));
1771+
assertThat(bean.streamTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
17261772
bf.getBean("testBean1", TestBean.class));
1773+
assertThat(bf.containsSingleton("testBean3")).isFalse();
1774+
assertThat(bean.plainTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1775+
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean3", TestBean.class));
1776+
assertThat(bean.plainTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1777+
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean3", TestBean.class));
1778+
assertThat(bf.containsSingleton("testBean4")).isFalse();
1779+
assertThat(bean.allTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1780+
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean3", TestBean.class),
1781+
bf.getBean("testBean4", TestBean.class));
1782+
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1783+
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean3", TestBean.class),
1784+
bf.getBean("testBean4", TestBean.class));
17271785
}
17281786

17291787
@Test
@@ -3304,9 +3362,25 @@ public List<TestBean> streamTestBeans() {
33043362
return this.testBean.stream().toList();
33053363
}
33063364

3307-
public List<TestBean> sortedTestBeans() {
3365+
public List<TestBean> streamTestBeansInOrder() {
33083366
return this.testBean.orderedStream().toList();
33093367
}
3368+
3369+
public List<TestBean> plainTestBeans() {
3370+
return this.testBean.stream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)).toList();
3371+
}
3372+
3373+
public List<TestBean> plainTestBeansInOrder() {
3374+
return this.testBean.orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)).toList();
3375+
}
3376+
3377+
public List<TestBean> allTestBeans() {
3378+
return this.testBean.stream(ObjectProvider.UNFILTERED).toList();
3379+
}
3380+
3381+
public List<TestBean> allTestBeansInOrder() {
3382+
return this.testBean.orderedStream(ObjectProvider.UNFILTERED).toList();
3383+
}
33103384
}
33113385

33123386

0 commit comments

Comments
 (0)