Skip to content

Commit 65c8fa4

Browse files
committed
Consistent ordered list access and lazy streaming for ObjectProvider
Includes fallback match for collection/map dependency if qualified. Issue: SPR-17272 Issue: SPR-17197
1 parent 0685651 commit 65c8fa4

File tree

12 files changed

+303
-136
lines changed

12 files changed

+303
-136
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,15 +214,23 @@ public interface BeanFactory {
214214
* @param requiredType type the bean must match; can be an interface or superclass
215215
* @return a corresponding provider handle
216216
* @since 5.1
217+
* @see #getBeanProvider(ResolvableType)
217218
*/
218219
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
219220

220221
/**
221222
* Return an provider for the specified bean, allowing for lazy on-demand retrieval
222223
* of instances, including availability and uniqueness options.
223-
* @param requiredType type the bean must match; can be a generic type declaration
224+
* @param requiredType type the bean must match; can be a generic type declaration.
225+
* Note that collection types are not supported here, in contrast to reflective
226+
* injection points. For programmatically retrieving a list of beans matching a
227+
* specific type, specify the actual bean type as an argument here and subsequently
228+
* use {@link ObjectProvider#toList()} or its lazy streaming/iteration options.
224229
* @return a corresponding provider handle
225230
* @since 5.1
231+
* @see ObjectProvider#stream()
232+
* @see ObjectProvider#iterator()
233+
* @see ObjectProvider#toList()
226234
*/
227235
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
228236

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package org.springframework.beans.factory;
1818

1919
import java.util.Iterator;
20+
import java.util.List;
2021
import java.util.function.Consumer;
2122
import java.util.function.Supplier;
23+
import java.util.stream.Collectors;
2224
import java.util.stream.Stream;
2325

2426
import org.springframework.beans.BeansException;
@@ -35,6 +37,8 @@
3537
* @author Juergen Hoeller
3638
* @since 4.3
3739
* @param <T> the object type
40+
* @see BeanFactory#getBeanProvider
41+
* @see org.springframework.beans.factory.annotation.Autowired
3842
*/
3943
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
4044

@@ -137,8 +141,18 @@ default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
137141
}
138142

139143
/**
140-
* Return an {@link Iterator} over resolved object instances.
141-
* <p>The default implementation delegates to {@link #stream()}.
144+
* Return a sequential {@link Stream} over lazily resolved object instances,
145+
* without specific ordering guarantees (but typically in registration order).
146+
* @since 5.1
147+
* @see #iterator()
148+
*/
149+
default Stream<T> stream() {
150+
throw new UnsupportedOperationException("Multi-element access not supported");
151+
}
152+
153+
/**
154+
* Return an {@link Iterator} over lazily resolved object instances,
155+
* without specific ordering guarantees (but typically in registration order).
142156
* @since 5.1
143157
* @see #stream()
144158
*/
@@ -148,15 +162,17 @@ default Iterator<T> iterator() {
148162
}
149163

150164
/**
151-
* Return a sequential {@link Stream} over resolved object instances.
152-
* <p>The default implementation returns a stream of one element or an
153-
* empty stream if not available, resolved via {@link #getIfAvailable()}.
165+
* Return a {@link List} with fully resolved object instances,
166+
* potentially pre-ordered according to a common comparator.
167+
* <p>In a common Spring application context, this will be ordered
168+
* according to {@link org.springframework.core.Ordered} /
169+
* {@link org.springframework.core.annotation.Order} conventions,
170+
* analogous to multi-element injection points of list/array type.
154171
* @since 5.1
155-
* @see #iterator()
172+
* @see #stream()
156173
*/
157-
default Stream<T> stream() {
158-
T instance = getIfAvailable();
159-
return (instance != null ? Stream.of(instance) : Stream.empty());
174+
default List<T> toList() {
175+
return stream().collect(Collectors.toList());
160176
}
161177

162178
}

spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,21 @@ public boolean isRequired(DependencyDescriptor descriptor) {
325325
return (autowired == null || autowired.required());
326326
}
327327

328+
/**
329+
* Determine whether the given dependency declares a qualifier annotation.
330+
* @see #isQualifier(Class)
331+
* @see Qualifier
332+
*/
333+
@Override
334+
public boolean hasQualifier(DependencyDescriptor descriptor) {
335+
for (Annotation ann : descriptor.getAnnotations()) {
336+
if (isQualifier(ann.annotationType())) {
337+
return true;
338+
}
339+
}
340+
return false;
341+
}
342+
328343
/**
329344
* Determine whether the given dependency declares a value annotation.
330345
* @see Value

spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -199,23 +199,6 @@ public boolean isEager() {
199199
return this.eager;
200200
}
201201

202-
/**
203-
* Return whether this descriptor allows for stream-style access to
204-
* result instances.
205-
* <p>By default, dependencies are strictly resolved to the declaration of
206-
* the injection point and therefore only resolve multiple entries if the
207-
* injection point is declared as an array, collection or map. This is
208-
* indicated by returning {@code false} here.
209-
* <p>Overriding this method to return {@code true} indicates that the
210-
* injection point declares the bean type but the resolution is meant to
211-
* end up in a {@link java.util.stream.Stream} for the declared bean type,
212-
* with the caller handling the multi-instance case for the injection point.
213-
* @since 5.1
214-
*/
215-
public boolean isStreamAccess() {
216-
return false;
217-
}
218-
219202
/**
220203
* Resolve the specified not-unique scenario: by default,
221204
* throwing a {@link NoUniqueBeanDefinitionException}.

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -57,6 +57,20 @@ default boolean isRequired(DependencyDescriptor descriptor) {
5757
return descriptor.isRequired();
5858
}
5959

60+
/**
61+
* Determine whether the given descriptor declares a qualifier beyond the type
62+
* (typically - but not necessarily - a specific kind of annotation).
63+
* <p>The default implementation returns {@code false}.
64+
* @param descriptor the descriptor for the target method parameter or field
65+
* @return whether the descriptor declares a qualifier, narrowing the candidate
66+
* status beyond the type match
67+
* @since 5.1
68+
* @see org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#hasQualifier
69+
*/
70+
default boolean hasQualifier(DependencyDescriptor descriptor) {
71+
return false;
72+
}
73+
6074
/**
6175
* Determine whether a default value is suggested for the given dependency.
6276
* <p>The default implementation simply returns {@code null}.

0 commit comments

Comments
 (0)