Walk through the registerBean overload that takes a Supplier and BeanDefinitionCustomizer varargs. What does each argument do and what can you customize?
answer
- instanceSupplier on RootBeanDefinition
- BeanDefinitionCustomizer = void customize(bd)
- setScope/setPrimary/setLazyInit
- customizers apply in order, last wins
- String-name overload for deterministic names
basics
~20 sregisterBean(Class, Supplier, BeanDefinitionCustomizer...): the Class is the bean type, the Supplier is a lambda that creates the instance, and each BeanDefinitionCustomizer is a lambda that tweaks the BeanDefinition — e.g. set scope, primary, or lazy.
solid answer
~40 sThe signature is registerBean(Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers) on GenericApplicationContext. Internally it builds a RootBeanDefinition for the class, sets its instanceSupplier to your lambda, applies each customizer, and registers it (there is also a String-name overload; without a name Spring generates one). BeanDefinitionCustomizer is a functional interface — void customize(BeanDefinition bd) — so each customizer receives the freshly built definition and can call bd.setScope(SCOPE_PROTOTYPE), bd.setPrimary(true), bd.setLazyInit(true), bd.setAutowireCandidate(false), bd.setDescription(...), or set qualifiers/attributes. Customizers run in order, so later ones can override earlier ones. The Supplier owns instantiation, so it is where you inject collaborators manually — commonly by pulling them from the context or capturing them in the closure. This overload is the workhorse for reflection-free, explicit registration.
code
java · 13 linesGenericApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBean(
"paymentClient", // explicit name
PaymentClient.class, // bean type
() -> new PaymentClient(apiKey), // instance supplier (no autowiring)
bd -> bd.setScope("singleton"), // customizer 1
bd -> bd.setPrimary(true), // customizer 2
bd -> bd.setLazyInit(true) // customizer 3
);
ctx.refresh();
PaymentClient c = ctx.getBean(PaymentClient.class);go deeper
Name the three arguments and that customizers set scope/primary/lazy.
Explain instanceSupplier, the functional BeanDefinitionCustomizer interface, ordering, and the name-generation overload.
Discuss dependency-injection patterns with a supplier and casting to AbstractBeanDefinition for qualifiers/autowire mode.
Weigh supplier-based control vs autowiring for AOT, and design guidance for registering same-type beans deterministically.
**Signature (on `GenericApplicationContext`)** ```java <T> void registerBean(Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers) <T> void registerBean(String beanName, Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers) ``` There are also supplier-less overloads (`registerBean(Class, Object... constructorArgs)`) that let the container instantiate and autowire. **Argument by argument** 1. **`beanClass`** — the type registered; used for type-based lookup/injection (`getBean(MyType.class)`, `@Autowired MyType`). A `RootBeanDefinition` is created for it. 2. **`supplier`** — a `Supplier<T>` set as the definition's **`instanceSupplier`**. When the bean is needed, Spring calls `supplier.get()` instead of instantiating via constructor reflection. This means **no autowiring of constructor args** — the supplier is fully responsible for building the object. 3. **`customizers`** — zero or more `BeanDefinitionCustomizer`. This is a functional interface: ```java @FunctionalInterface public interface BeanDefinitionCustomizer { void customize(BeanDefinition bd); } ``` Each is invoked with the built definition, in argument order. Typical calls: - `bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)` / `"singleton"` - `bd.setPrimary(true)` - `bd.setLazyInit(true)` - `bd.setAutowireCandidate(false)` - `bd.setInitMethodName(...)` / `bd.setDestroyMethodName(...)` - `bd.setDescription(...)`, `bd.setAttribute(key, value)` - add qualifiers via `((AbstractBeanDefinition) bd).addQualifier(new AutowireCandidateQualifier(...))` **Name generation** — the no-name overload generates a bean name (the framework's `BeanNameGenerator` — typically the decapitalized simple class name). Use the String-name overload for a deterministic name (important when two beans share a type). **Ordering & override semantics** — customizers apply left-to-right; if two set the same property, the last wins. Registration itself must happen **before** `refresh()`. **How dependencies get in with a Supplier** — three common patterns: - Closure capture: `() -> new Svc(depAlreadyInScope)`. - Pull from context: `() -> new Svc(ctx.getBean(Dep.class))` (needs a reference to the context). - Use the supplier-less overload so Spring autowires instead. **Gotchas** - Passing a `Supplier` **disables constructor autowiring**; forgetting this leads to NPEs on collaborators you assumed were injected. - `BeanDefinitionCustomizer` cannot 'unset' the instanceSupplier's control over construction — it edits definition metadata, not the instantiation strategy. - Some rich customizations (qualifiers, autowire mode) require casting to `AbstractBeanDefinition`. - Prototype scope + supplier means the supplier runs on every `getBean` call. **When to use** — precise, code-driven registration where you want explicit scope/primary/lazy control without annotations; registering the same type multiple times under different names; AOT/native-friendly wiring.
- If two customizers both call setScope, which wins?The last one in the varargs order, because customizers are applied left-to-right against the same BeanDefinition and simply overwrite the property.
- How would you make a supplier-registered bean lazy and non-autowire-candidate?Pass customizers: bd -> bd.setLazyInit(true) and bd -> bd.setAutowireCandidate(false). Both edit the BeanDefinition metadata before registration.
saying these in an interview costs you the question
- Believing the Supplier bean still gets constructor injection
- Thinking BeanDefinitionCustomizer changes the instance rather than the definition
- Assuming registration works after refresh()
- Not knowing customizers are ordered / last-write-wins