How does the @Lookup annotation work, and what are the requirements on the method and bean it is used on?
answer
- @Lookup = Spring writes the method body
- CGLIB subclass overrides -> getBean each call
- target by return type, or @Lookup("name")
- no final/private/static; class not final
- factory-method beans not subclassed
basics
~10 sYou annotate a method (usually abstract) with @Lookup. Spring generates a CGLIB subclass that overrides it to return a fresh bean from the container each call. The class and method can't be final/private/static.
solid answer
~50 s`@Lookup` (org.springframework.beans.factory.annotation.Lookup) marks a method whose implementation Spring supplies. At startup Spring detects the annotation and creates a **CGLIB subclass** of the bean; the subclass overrides the method to call `getBean(...)`, returning a newly resolved instance on every call — ideal for pulling a prototype into a singleton. The target bean is chosen by the method's **return type**, or by name if you give `@Lookup("beanName")`. Requirements follow from CGLIB subclassing: the method can be **abstract** (Spring provides the body) or concrete (Spring overrides a dummy body), but must **not** be `final`, `private`, or `static`, and the enclosing class must not be `final`. The bean must be instantiable by CGLIB — component-scanned beans with a usable constructor work; beans created via `@Bean` factory methods generally don't. Arguments on the lookup method are forwarded to the `getBean(name, args)` call.
go deeper
Know that @Lookup returns a fresh bean and the method is usually abstract.
Must state the CGLIB-subclass mechanism, the no-final/private/static rules, and by-type vs by-name resolution.
Should note factory-method beans aren't subclassed and how args are forwarded to getBean.
Should reason about testability, why ObjectProvider is often preferable, and CGLIB constraints across the app.
## What @Lookup is `@Lookup` lives in `org.springframework.beans.factory.annotation`. You place it on a method of a Spring-managed bean, and **Spring writes the method body for you**. Every invocation performs a container lookup and returns a fresh bean — the canonical use being a **prototype fetched into a singleton**. ```java @Component public abstract class ReportService { public byte[] handle(Data d) { ReportBuilder builder = newBuilder(); // fresh prototype each call return builder.build(d); } @Lookup protected abstract ReportBuilder newBuilder(); } ``` ```java @Component @Scope("prototype") class ReportBuilder { /* ... */ } ``` ## How Spring implements it During bean creation Spring's `AutowiredAnnotationBeanPostProcessor` detects `@Lookup` methods and marks the bean as needing method override. The container then uses **CGLIB** to generate a runtime **subclass** of `ReportService`, overriding `newBuilder()` so its body effectively does `getBean(ReportBuilder.class)` (a prototype, so a new instance each time). The actual bean instance you get is an instance of this generated subclass. ## Selecting the target bean - **By return type (default):** `@Lookup` with no value resolves a bean assignable to the method's return type. - **By name:** `@Lookup("reportBuilder")` resolves the named bean explicitly (useful when several candidates match the type). ## Method / class constraints (all stem from CGLIB subclassing) - The method may be **`abstract`** (then the class is abstract too — Spring makes it concrete) or a **concrete non-final** method (Spring overrides it; any hand-written body is ignored). - The method must **not** be `final`, `private`, or `static` — none of those can be overridden. - The enclosing **class must not be `final`**. - The bean must be **CGLIB-instantiable**: a component-scanned class with an accessible (often no-arg) constructor works. Beans defined by `@Bean` factory methods are **not** subclassed for `@Lookup` and typically won't get the override. ## Passing arguments A `@Lookup` method may declare parameters; Spring forwards them to `getBean(beanName, args)`, letting them be used as constructor arguments of the looked-up prototype. If you don't need this, keep the method no-arg. ## Common gotchas - **Nothing overridden / stale instance:** usually the class or method is `final`, or the bean was built by a factory method, so CGLIB couldn't subclass it. - **Returning a singleton:** if the target bean is itself a singleton, `@Lookup` returns the same shared instance each call — the fresh-instance benefit only appears with prototype (or other shorter) scopes. - **Testing:** because Spring subclasses the bean, plain `new ReportService()` in a unit test leaves the abstract method unimplemented; test through the container or override the method manually. ## Relationship to alternatives `@Lookup` is the annotation-driven twin of XML `<lookup-method>`. A cleaner modern alternative is injecting `ObjectProvider<ReportBuilder>` and calling `getObject()` — no CGLIB, no abstract methods — but `@Lookup` keeps the call site as an ordinary method invocation.
- Why can't a @Lookup method be private, static, or final?Spring implements @Lookup by generating a CGLIB subclass that overrides the method. Private, static, and final methods cannot be overridden by a subclass, so CGLIB can't supply the container-lookup body.
- How does @Lookup decide which bean to return?By default it resolves a bean whose type matches the method's return type. You can override that with @Lookup("beanName") to target a specific bean by name when the type is ambiguous.
saying these in an interview costs you the question
- Saying @Lookup uses a JDK dynamic proxy (it needs CGLIB subclassing, not interface proxies)
- Claiming you must write the method body yourself
- Thinking @Lookup works on beans created by @Bean factory methods
- Believing a final method can be used with @Lookup