skip to content

ObjectProvider, Optional & Collection Injection

Injecting every bean of a type as a List or Map, ranking them with @Order, and using ObjectProvider or Optional for dependencies that may be absent or must be fetched lazily. This is the idiomatic answer to the plugin-style design questions interviewers like to pose.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

How do you inject a dependency into a Spring bean that may not exist in the context, without the application failing to start?

level: juniorimportance: must knowfreq 62%

answer

  1. required=true is the default → missing bean = startup failure
  2. Optional<T> → Optional.empty() when absent
  3. @Autowired(required=false) / @Nullable → null
  4. ObjectProvider.getIfAvailable() → null or default supplier
  5. NoSuchBeanDefinitionException is what you're avoiding

basics

~10 s

Make the dependency optional. Use Optional<T>, or @Autowired(required=false), or ObjectProvider<T>. If no bean exists Spring injects an empty Optional / null / an empty provider instead of throwing NoSuchBeanDefinitionException.

solid answer

~40 s

By default @Autowired is required=true, so a missing bean throws NoSuchBeanDefinitionException at startup. To make a dependency optional you have three main options. (1) Declare the parameter/field as java.util.Optional<T> — Spring injects Optional.empty() when no candidate exists. (2) Use @Autowired(required=false) or the @Nullable annotation on the field/parameter, and Spring leaves it null. (3) Inject ObjectProvider<T> and call getIfAvailable(), which returns null (or a supplied default) when absent. Optional and ObjectProvider are preferred over required=false because they are explicit at the type level and never leave a raw null that a caller might dereference by accident. ObjectProvider additionally handles the multiple-candidate case gracefully.

code

java · 20 lines
java
@Service
public class ReportService {

    private final Optional<Watermarker> watermarker;      // empty if no bean
    private final ObjectProvider<AuditSink> auditSink;    // lazy, optional

    public ReportService(Optional<Watermarker> watermarker,
                         ObjectProvider<AuditSink> auditSink) {
        this.watermarker = watermarker;
        this.auditSink = auditSink;
    }

    public Report build() {
        Report r = new Report();
        watermarker.ifPresent(w -> w.apply(r));
        // default to a no-op sink when none is registered
        auditSink.getIfAvailable(NoopAuditSink::new).record("report.built");
        return r;
    }
}

go deeper

for a junior

Know the three ways: Optional<T>, @Autowired(required=false)/@Nullable, ObjectProvider, and that the default is a hard failure.

for a middle

Explain why Optional/ObjectProvider are safer than a raw null, and the constructor required=false skip behavior.

for a senior

Contrast zero-candidate vs multiple-candidate handling and when ObjectProvider.getIfUnique wins.

for a principal

Discuss how these interact with null-safety annotations, Kotlin nullable types, and API design for optional collaborators.

## The problem Spring's `@Autowired` defaults to `required = true`. When the container tries to satisfy such an injection point and finds **zero** matching beans, it throws `org.springframework.beans.factory.NoSuchBeanDefinitionException` and the `ApplicationContext` fails to refresh — the app won't start. Sometimes a dependency is genuinely optional (a feature that may be disabled, an add-on module that may be absent). You need a way to say "inject it if present, otherwise carry on." ## The four mechanisms ### 1. `java.util.Optional<T>` Declare the injection point as `Optional<MyService>`. If a bean is present Spring wraps it in `Optional.of(bean)`; if not, it injects `Optional.empty()`. This is the most self-documenting form — the type itself announces "may be absent" and forces callers to handle emptiness. ```java @Autowired private Optional<AuditSink> auditSink; ``` ### 2. `@Autowired(required = false)` Spring skips the injection if no candidate is found, leaving the field/parameter as `null` (for reference types). Gotcha: on a **constructor**, if any one `@Autowired(required=false)` constructor param cannot be resolved, Spring skips that whole constructor rather than injecting null. For field/setter injection the field stays null. ### 3. `@Nullable` Annotating a parameter with `org.springframework.lang.@Nullable` (or JSR-305 `@Nullable`) has the same effect as `required=false` for that single parameter: Spring injects null when absent. Works cleanly on individual constructor parameters. ### 4. `ObjectProvider<T>` `org.springframework.beans.factory.ObjectProvider<T>` is a lazy handle to the bean. `getIfAvailable()` returns the bean or `null` when absent; `getIfAvailable(Supplier<T> default)` returns a fallback; `ifAvailable(Consumer<T>)` runs the consumer only if present. Because the lookup is deferred to method-call time, the provider itself is always injectable even when the target bean does not exist. ```java private final ObjectProvider<AuditSink> auditSinkProvider; AuditSink sink = auditSinkProvider.getIfAvailable(NoopAuditSink::new); ``` ## Which to use - `Optional<T>` — cleanest for a single optional collaborator you resolve once at construction. - `ObjectProvider<T>` — when you also need lazy resolution, defaults, multiple candidates, or fresh prototype instances. - `@Nullable` / `required=false` — lightweight but leaves a raw null; least safe. ## Edge cases & gotchas - If **multiple** candidates exist, `Optional<T>` and a plain `T` both throw `NoUniqueBeanDefinitionException` unless one is `@Primary` or a `@Qualifier` narrows it. `ObjectProvider.getIfUnique()` instead returns null on ambiguity — but candidate disambiguation via @Qualifier/@Primary is a separate topic. - `Optional` is unwrapped by Spring itself — you never inject `Optional<Optional<T>>`. - Constructor injection is generally preferred; `Optional` and `ObjectProvider` both work as constructor parameters.

  • What happens with Optional<T> if there are two beans of type T and neither is @Primary?
    It still throws NoUniqueBeanDefinitionException. Optional only handles the zero-candidate case, not ambiguity. You'd need a @Qualifier, @Primary, or ObjectProvider.getIfUnique() (which returns null on ambiguity).
  • Does @Autowired(required=false) on a constructor parameter inject null?
    Not straightforwardly — if a required=false constructor cannot be fully satisfied Spring skips that constructor. For guaranteed null-on-absence of a single param, use @Nullable on that parameter, or field/setter injection.

saying these in an interview costs you the question

  • Thinking a missing @Autowired bean silently injects null by default (it throws)
  • Believing Optional<T> resolves the multiple-candidate ambiguity
  • Claiming you must inject Optional<Optional<T>> — Spring unwraps one level
  • Saying ObjectProvider throws at injection time when the bean is absent

context

open as a page

How do you inject all beans of a given type at once, and what do you get when you use List, Set, or Map<String,T>?

level: middleimportance: must knowfreq 58%

basics

~20 s

Autowire a List<T>, Set<T>, or Map<String,T>. Spring injects every bean of type T. For the Map the keys are the bean names and the values are the beans. This is the standard way to implement plugin/strategy registries.

open as a page

When you inject a List<T> of beans, how do you control their order, and how do @Order, the Ordered interface, and orderedStream() relate?

level: middleimportance: should knowfreq 45%

basics

~20 s

Add @Order(n) to each bean or implement Ordered.getOrder(). Spring sorts injected List/Set/array by that value — lower number = higher priority = earlier. For ObjectProvider use orderedStream(), which applies the same ranking; plain stream() does not.

open as a page

What is ObjectProvider<T> and how do getIfAvailable(), getIfUnique(), getObject(), and stream() differ? When would you choose it over plain or Optional injection?

level: seniorimportance: should knowfreq 48%

basics

~20 s

ObjectProvider<T> is a lazy handle to a bean. getObject() throws if none/multiple; getIfAvailable() returns null if none (throws if multiple); getIfUnique() returns null if none OR multiple non-primary; stream()/orderedStream() give all matches. Use it for optional, ambiguous, lazy, or prototype lookups.

open as a page

What does @Lazy do when placed on an injection point (not a bean definition), and how does the resulting proxy behave? When is it the right tool?

level: principalimportance: should knowfreq 34%

basics

~20 s

@Lazy on an injection point injects a lazy-resolution proxy instead of the real bean. Spring resolves the actual bean only on the first method call through the proxy. It's used to break circular dependencies, defer expensive initialization, or inject a shorter-scoped bean.

open as a page