Backend Development 8 min read

Mastering Spring’s ObjectProvider: Flexible Dependency Injection Techniques

Spring’s ObjectProvider interface enhances dependency injection by allowing optional, lazy, and multiple bean retrieval, offering methods like getIfAvailable, orderedStream, and stream, with code examples demonstrating on-demand injection, fetching all implementations, and the underlying mechanism via DependencyObjectProvider in the DefaultListableBeanFactory.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring’s ObjectProvider: Flexible Dependency Injection Techniques

Environment: Spring 5.3.23

1. Introduction to ObjectProvider

ObjectProvider is an interface in the Spring framework that extends ObjectFactory and is designed for injection points. It separates object creation from injection, making code clearer and more maintainable, and supports polymorphism for various injection scenarios.

It is a variant of ObjectFactory that allows optional and non-unique handling. Since version 5.1 it also extends Iterable and provides Stream support, enabling for‑each iteration and collection‑style access. Core methods include:

<code>ObjectProvider&lt;T&gt; extends ObjectFactory&lt;T&gt;, Iterable&lt;T&gt; {</code>
<code>  T getObject(Object... args) throws BeansException;</code>
<code>  @Nullable T getIfAvailable() throws BeansException;</code>
<code>  default T getIfAvailable(Supplier&lt;T&gt; defaultSupplier) throws BeansException {</code>
<code>    T dependency = getIfAvailable();</code>
<code>    return (dependency != null ? dependency : defaultSupplier.get());</code>
<code>  }</code>
<code>  default void ifAvailable(Consumer&lt;T&gt; dependencyConsumer) throws BeansException {</code>
<code>    T dependency = getIfAvailable();</code>
<code>    if (dependency != null) {</code>
<code>      dependencyConsumer.accept(dependency);</code>
<code>    }</code>
<code>  }</code>
<code>  @Nullable T getIfUnique() throws BeansException;</code>
<code>  default T getIfUnique(Supplier&lt;T&gt; defaultSupplier) throws BeansException {</code>
<code>    T dependency = getIfUnique();</code>
<code>    return (dependency != null ? dependency : defaultSupplier.get());</code>
<code>  }</code>
<code>  default void ifUnique(Consumer&lt;T&gt; dependencyConsumer) throws BeansException {</code>
<code>    T dependency = getIfUnique();</code>
<code>    if (dependency != null) {</code>
<code>      dependencyConsumer.accept(dependency);</code>
<code>    }</code>
<code>  }</code>
<code>  default Iterator&lt;T&gt; iterator() { return stream().iterator(); }</code>
<code>  default Stream&lt;T&gt; stream() { throw new UnsupportedOperationException("Multi element access not supported"); }</code>
<code>  default Stream&lt;T&gt; orderedStream() { throw new UnsupportedOperationException("Ordered element access not supported"); }</code>
<code>}</code>

2. Using ObjectProvider for Dependency Injection

Case 1: On‑demand injection

<code>public interface MessageService {</code>
<code>  void sendMessage(String message);</code>
<code>}</code>
<code>@Service</code>
<code>public class SmsMessageServiceImpl implements MessageService {</code>
<code>  @Override</code>
<code>  public void sendMessage(String message) {</code>
<code>    System.out.printf("发送短信:%s%n", message);</code>
<code>  }</code>
<code>}</code>

Using ObjectProvider for on‑demand injection:

<code>@Component</code>
<code>public class MessageServiceConsumer {</code>
<code>  private final ObjectProvider&lt;MessageService&gt; messageServiceProvider;</code>
<code>  public MessageServiceConsumer(ObjectProvider&lt;MessageService&gt; messageServiceProvider) {</code>
<code>    this.messageServiceProvider = messageServiceProvider;</code>
<code>  }</code>
<code>  public void sendMessageByType(String messageType, String message) {</code>
<code>    try {</code>
<code>      MessageService messageService = messageServiceProvider.getIfAvailable();</code>
<code>      messageService.sendMessage(message);</code>
<code>    } catch (NoSuchBeanException e) {</code>
<code>      System.err.printf("不存在Bean, %s%n", e.getMessage());</code>
<code>    } catch (NoUniqueBeanDefinitionException e) {</code>
<code>      System.err.printf("存在多个Bean: %s%n", e.getMessage());</code>
<code>    }</code>
<code>  }</code>
<code>}</code>

Case 2: Retrieving all instances of a type

<code>@Service</code>
<code>public class EmailMessageServiceImpl implements MessageService {</code>
<code>  @Override</code>
<code>  public void sendMessage(String message) {</code>
<code>    System.out.printf("发送Email:%s%n", message);</code>
<code>  }</code>
<code>}</code>
<code>@Component</code>
<code>public class MessageServiceConsumer2 {</code>
<code>  private final ObjectProvider&lt;MessageService&gt; messageServiceProvider;</code>
<code>  public MessageServiceConsumer2(ObjectProvider&lt;MessageService&gt; messageServiceProvider) {</code>
<code>    this.messageServiceProvider = messageServiceProvider;</code>
<code>  }</code>
<code>  public void sendMessage(String message) {</code>
<code>    // Retrieve all matching beans in order</code>
<code>    this.messageServiceProvider.orderedStream().forEach(ms -> ms.sendMessage(message));</code>
<code>  }</code>
<code>}</code>

The order of beans returned by orderedStream() can be defined with the @Order annotation or by implementing Ordered :

<code>@Service</code>
<code>@Order(2)</code>
<code>public class EmailMessageServiceImpl implements MessageService {}</code>
<code>@Service</code>
<code>@Order(1)</code>
<code>public class SmsMessageServiceImpl implements MessageService {}</code>

Thus the stream order will be SmsMessageServiceImpl followed by EmailMessageServiceImpl .

3. ObjectProvider Internals

The core handling of dependency injection resides in DefaultListableBeanFactory#resolveDependency :

<code>public class DefaultListableBeanFactory {</code>
<code>  public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,</code>
<code>      @Nullable Set&lt;String&gt; autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {</code>
<code>    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());</code>
<code>    if (Optional.class == descriptor.getDependencyType()) {</code>
<code>      return createOptionalDependency(descriptor, requestingBeanName);</code>
<code>    }</code>
<code>    // ObjectProvider inherits from ObjectFactory</code>
<code>    // Directly returns a DependencyObjectProvider implementation</code>
<code>    else if (ObjectFactory.class == descriptor.getDependencyType() ||</code>
<code>        ObjectProvider.class == descriptor.getDependencyType()) {</code>
<code>      return new DependencyObjectProvider(descriptor, requestingBeanName);</code>
<code>    }</code>
<code>    // ...</code>
<code>  }</code>
<code>}</code>

The returned DependencyObjectProvider performs actual bean lookup only when its specific methods are invoked:

<code>private class DependencyObjectProvider implements BeanObjectProvider&lt;Object&gt; {</code>
<code>  public Object getObject() throws BeansException {</code>
<code>    // Resolve the dependency lazily</code>
<code>    Object result = doResolveDependency(this.descriptor, this.beanName, null, null);</code>
<code>    return result;</code>
<code>  }</code>
<code>  public Stream&lt;Object&gt; orderedStream() {</code>
<code>    return resolveStream(true);</code>
<code>  }</code>
<code>  private Stream&lt;Object&gt; resolveStream(boolean ordered) {</code>
<code>    DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, ordered);</code>
<code>    Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);</code>
<code>    return (result instanceof Stream ? (Stream&lt;Object&gt;) result : Stream.of(result));</code>
<code>  }</code>
<code>}</code>

End of article.

backendJavaSpringDependency InjectionObjectProvider
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.