When would you choose @Lookup / lookup-method over ObjectFactory/Provider (and vice versa) for obtaining prototype beans?
answer
- @Lookup = hidden lookup behind plain method, needs CGLIB
- ObjectProvider/Provider = injected field, getObject()/get()
- Provider<T> = JSR-330 portable
- ObjectProvider adds getIfAvailable/getIfUnique/stream
- modern default = ObjectProvider; both need prototype target
basics
~20 sBoth give a singleton fresh prototypes. @Lookup keeps a clean method call but needs CGLIB and non-final abstract-ish methods. ObjectFactory/Provider is a plain injected field with getObject()/get() — no CGLIB, easier to test, usually preferred today.
solid answer
~40 sBoth solve the singleton-needs-fresh-prototype problem. `@Lookup`/`<lookup-method>` have the container **override a method** (via CGLIB subclass) so the call site reads like a normal method returning a new bean — no explicit factory type leaks into your code. Costs: it needs CGLIB, forbids `final`/`private`/`static` methods and final classes, doesn't work on factory-method beans, and complicates unit testing since the real bean is an abstract-ish subclass. `ObjectFactory<T>` (Spring) / `Provider<T>` (JSR-330) / `ObjectProvider<T>` are **injected as normal dependencies**; you call `getObject()`/`get()` to pull a fresh instance. No CGLIB, no method-override constraints, trivially mockable in tests, and `ObjectProvider` adds null-safe/optional/stream helpers. The tradeoff is a visible factory abstraction at the call site. Modern guidance favors `ObjectProvider`; `@Lookup` suits cases where you want the lookup hidden behind a plain method or must keep an existing method signature.
go deeper
Know both exist and both give fresh prototypes.
Can show the getObject()/get() call and that no CGLIB is involved for the provider style.
Must weigh testability, CGLIB constraints, factory-method beans, and ObjectProvider's extra helpers to justify a choice.
Should set a team default (ObjectProvider), note JSR-330 portability, and flag that scope of the target still governs freshness.
## Same goal, two styles Both mechanisms let a **long-lived (singleton) bean** obtain a **fresh shorter-lived (prototype) bean** on demand, avoiding the 'resolved once' trap and avoiding direct `ApplicationContext` coupling. ### Method injection: @Lookup / <lookup-method> ```java @Component abstract class OrderProcessor { void process(Order o) { validator().check(o); } // fresh each call @Lookup protected abstract OrderValidator validator(); } ``` - **Call site:** an ordinary method call; no factory type appears in your business code. - **Mechanism:** Spring generates a **CGLIB subclass** overriding the method to call `getBean(...)`. - **Constraints:** class not `final`; method not `final`/`private`/`static`; bean must be CGLIB-instantiable (component-scanned, not a `@Bean` factory method). - **Testing pain:** the bean is effectively abstract; `new OrderProcessor()` won't compile/instantiate cleanly, so you test through the container or override the method by hand. ### Provider style: ObjectFactory / Provider / ObjectProvider ```java @Component class OrderProcessor { private final ObjectProvider<OrderValidator> validators; OrderProcessor(ObjectProvider<OrderValidator> validators) { this.validators = validators; } void process(Order o) { validators.getObject().check(o); } // fresh each call } ``` - **`ObjectFactory<T>`** — Spring interface, `getObject()`. - **`javax/jakarta.inject.Provider<T>`** — JSR-330 standard, `get()` (portable across DI frameworks). - **`ObjectProvider<T>`** — Spring's richer subtype adding `getIfAvailable()`, `getIfUnique()`, `stream()`, and Iterable/Stream support for optional/multiple candidates. - **Mechanism:** just a normal injected bean; **no CGLIB**, no subclassing. - **Constraints:** none of the method-override rules apply; works on any bean, including factory-method beans. - **Testing:** trivial — pass a lambda or mock `ObjectProvider`/`Provider` in a plain constructor call. ## Decision guide | Consideration | Prefer @Lookup | Prefer ObjectProvider/Provider | |---|---|---| | Want call site to look like a plain method | Yes | No (explicit getObject/get) | | Need easy unit testing / no CGLIB | No | Yes | | Bean is created by a @Bean factory method | Won't work | Works | | Need optional/multiple-candidate handling | No | Yes (ObjectProvider) | | Want a JSR-330 portable API | No | Provider<T> | | Class/method must stay final | Won't work | Works | ## Practical recommendation Modern Spring guidance leans toward **`ObjectProvider`** (or `Provider` for portability): it's explicit, testable, CGLIB-free, and handles optional/plural beans. Reach for **`@Lookup`** when you specifically want the lookup hidden behind a normal method (e.g., an existing abstract API you must implement) or in codebases already using that idiom. `<lookup-method>` is the XML equivalent for XML-configured apps. ## Gotcha to remember With **any** of these, the fresh-instance benefit only materializes if the target bean is actually prototype-scoped (or another short scope). If it's a singleton, every `getObject()`/`@Lookup` call returns the same shared instance.
- What does ObjectProvider offer beyond plain ObjectFactory?Null-safe and optional-aware helpers: getIfAvailable(), getIfUnique(), plus stream()/iterator() over multiple candidate beans. Plain ObjectFactory only has getObject(), which throws if the bean is missing or ambiguous.
- Which option keeps your code framework-portable, and why?javax/jakarta.inject.Provider<T> (JSR-330). It's a standard DI API, so the same code compiles against other DI containers, whereas ObjectFactory/ObjectProvider and @Lookup are Spring-specific.
saying these in an interview costs you the question
- Claiming ObjectProvider/Provider requires CGLIB like @Lookup does
- Saying getObject() on a singleton-scoped target yields new instances
- Asserting @Lookup is always preferable to ObjectProvider
- Confusing ObjectFactory (getObject) with Provider (get)