What problems does JSR-330 Provider<T> solve, and how does Spring implement it?
answer
- Provider<T>.get() = lazy, on-demand
- fresh prototype per get() inside singleton
- breaks construction-time cycles
- ObjectProvider = Spring's richer variant
- Provider.get() throws; getIfAvailable doesn't
basics
~10 sProvider<T> gives lazy, on-demand access to a bean via provider.get() instead of injecting the instance eagerly. It helps with optional beans, prototype-scoped beans in singletons, and breaking circular dependencies.
solid answer
~40 sjakarta.inject.Provider<T> is a factory interface with a single get() method. Instead of injecting a T directly, you inject a Provider<T> and call get() when you actually need the instance. This defers resolution to call time, which solves three classic problems: (1) getting a fresh prototype-scoped bean each time inside a singleton — calling get() yields a new instance per call; (2) lazy or optional lookup, deferring a possibly-expensive or not-yet-available bean; (3) breaking certain circular dependencies by not requiring the collaborator at construction time. Spring satisfies Provider<T> injection points automatically — no special config. Spring's own ObjectProvider and ObjectFactory offer the same idea plus richer methods (getIfAvailable, getIfUnique, stream). Provider is the portable standard variant.
code
java · 21 linesimport jakarta.inject.Provider;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
class Task { /* new instance wanted each time */ }
@Component
class TaskRunner {
private final Provider<Task> taskProvider;
TaskRunner(Provider<Task> taskProvider) { // injected once
this.taskProvider = taskProvider;
}
void runOnce() {
Task task = taskProvider.get(); // FRESH prototype instance every call
// ... use task ...
}
}go deeper
May just know 'Provider is lazy via get()'.
Explains the prototype-in-singleton use case.
Covers all three problems and contrasts Provider with ObjectProvider/@Lookup.
Discusses trade-offs, cycle-breaking as a smell, and portability vs ObjectProvider's richer semantics.
### The core interface `jakarta.inject.Provider<T>` (JSR-330) is trivially small: ```java public interface Provider<T> { T get(); } ``` Rather than injecting a `T`, you inject a `Provider<T>` and call `.get()` to obtain the instance **on demand**. Spring recognizes `Provider<T>` injection points out of the box and supplies a provider backed by the container. ### Problem 1 — a new prototype bean each time inside a singleton (scope mismatch) Spring beans are **singletons** by default; dependencies are injected **once** at construction. If a singleton needs a **prototype**-scoped collaborator and injects it directly, it captures a **single** instance forever — the prototype's 'new instance per request' semantics are lost. `Provider<T>` fixes this: inject `Provider<PrototypeBean>`, and every `provider.get()` asks the container for a **fresh** prototype instance. (Spring's own equivalents are `ObjectProvider`/`ObjectFactory`, and there's also method injection via `@Lookup`.) ### Problem 2 — lazy / deferred / optional access Direct injection resolves the dependency at container startup / bean creation. `Provider<T>` defers that to the first `get()` call. Useful when: - The bean is **expensive** to create and rarely used. - You want to avoid eager initialization ordering issues. - Combined with error handling, you can tolerate a bean that may not be present (though the standard `Provider.get()` throws if unresolvable; Spring's `ObjectProvider.getIfAvailable()` returns null instead — richer). ### Problem 3 — breaking circular dependencies If A needs B and B needs A via **constructor** injection, Spring can't build either — a `BeanCurrentlyInCreationException`. Injecting one side as `Provider<B>` means B isn't required at A's construction time; A calls `bProvider.get()` later, after both beans exist. This breaks the cycle. (Redesign is usually better, but Provider is a valid tool.) ### Spring's native equivalents - **`ObjectFactory<T>`** — Spring's older single-method factory, like Provider. - **`ObjectProvider<T>`** — extends ObjectFactory with `getIfAvailable()`, `getIfUnique()`, `orderedStream()`, `stream()`, and `Iterable` support. This is the **preferred Spring-native** choice; it handles zero-or-many candidates gracefully without exceptions. - **`@Lookup`** method injection — an abstract/overridable method Spring implements to return a fresh bean per call. `Provider<T>` is the **portable** (Guice/CDI/Spring) option; `ObjectProvider` is the more powerful Spring-only one. ### Gotchas - `Provider.get()` (standard) **throws** if the bean can't be resolved or is ambiguous — it has no getIfAvailable. Use `ObjectProvider` when you need null-safe / uniqueness-tolerant behavior. - Requires `jakarta.inject-api` on the classpath (same as @Inject/@Named). - Provider doesn't magically make a singleton 'reload' its own singleton dependency — the benefit is real only for prototype/request scopes or deferred/optional access. - Don't overuse it to paper over cyclic designs; prefer refactoring. ### When to use Reach for `Provider<T>` (or `ObjectProvider`) when a singleton must pull **fresh prototype/request-scoped** instances, when you need **lazy** access to an expensive bean, or to **break a construction-time cycle**. For Spring-only code prefer `ObjectProvider` for its richer API; use `Provider` when portability across DI containers matters.
- How does Spring's ObjectProvider improve on jakarta.inject.Provider?ObjectProvider adds getIfAvailable(), getIfUnique(), stream()/orderedStream() and Iterable support, so it tolerates zero or multiple candidate beans without throwing. Provider.get() throws if the bean is missing or ambiguous. Provider is the portable standard; ObjectProvider is Spring-only but richer.
- Without Provider, how else can a singleton get a fresh prototype instance each time?Via @Lookup method injection (Spring overrides an abstract method to return a new bean each call), by injecting the ApplicationContext/BeanFactory and calling getBean(), or by using ObjectFactory/ObjectProvider. Provider/@Lookup are cleaner than raw context lookup.
saying these in an interview costs you the question
- Thinking Provider makes a singleton dependency itself refresh
- Believing Provider.get() returns null when the bean is missing (it throws)
- Assuming you must configure Spring specially to inject Provider<T>