skip to content

What is BeanDefinitionBuilder and how do you use it to register a bean programmatically with a BeanDefinitionRegistry?

level: middleimportance: should knowfreq 30%

answer

  1. genericBeanDefinition(Class) fluent start
  2. addConstructorArgReference / addPropertyValue
  3. getBeanDefinition() -> AbstractBeanDefinition
  4. registry.registerBeanDefinition(name, bd)
  5. metadata-first vs supplier's instance-first

basics

~10 s

BeanDefinitionBuilder is a fluent helper to build a BeanDefinition. You call BeanDefinitionBuilder.genericBeanDefinition(MyBean.class), chain addPropertyValue/addConstructorArgValue/setScope, call getBeanDefinition(), then register it via registry.registerBeanDefinition(name, definition).

solid answer

~40 s

BeanDefinitionBuilder is a fluent builder for constructing BeanDefinition objects by hand. You start with a static factory — genericBeanDefinition(Class) or genericBeanDefinition(Class, Supplier) or rootBeanDefinition(...) — then chain calls: addConstructorArgValue / addConstructorArgReference for constructor injection, addPropertyValue / addPropertyReference for setter injection, setScope, setLazyInit, setInitMethodName, setAutowireMode, setFactoryMethod, etc. getBeanDefinition() returns the finished AbstractBeanDefinition. You then hand it to a BeanDefinitionRegistry — registry.registerBeanDefinition("beanName", bd). GenericApplicationContext and the DefaultListableBeanFactory implement BeanDefinitionRegistry, and it is also the registry passed to a BeanFactoryPostProcessor or ImportBeanDefinitionRegistrar. Unlike registerBean's supplier, references are wired as bean references resolved by the container. It is the lower-level, more expressive API when you need fine-grained control over the definition — including property values and references by name.

code

java · 21 lines
java
public class MyRegistrarProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        BeanDefinition dep = BeanDefinitionBuilder
            .genericBeanDefinition(JdbcTemplate.class)
            .addConstructorArgReference("dataSource")   // wire by bean name
            .getBeanDefinition();
        registry.registerBeanDefinition("reportJdbc", dep);

        BeanDefinition svc = BeanDefinitionBuilder
            .genericBeanDefinition(ReportService.class)
            .addConstructorArgReference("reportJdbc")
            .setScope(BeanDefinition.SCOPE_SINGLETON)
            .setLazyInit(true)
            .getBeanDefinition();
        registry.registerBeanDefinition("reportService", svc);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) { }
}

go deeper

for a junior

Know it fluently builds a BeanDefinition and you then register it with a registry.

for a middle

Distinguish arg value vs reference and property vs constructor injection, and name the registry registration step.

for a senior

Explain metadata-first vs supplier instance-first, and using it inside a BeanDefinitionRegistryPostProcessor.

for a principal

Reason about when named container-wired references beat hand-built suppliers, plus root/child definitions and post-processor ordering.

**What it is** `BeanDefinitionBuilder` (in `org.springframework.beans.factory.support`) is a fluent utility that assembles a `BeanDefinition` programmatically. A `BeanDefinition` is the container's *metadata* about a bean (class, scope, constructor args, property values, init/destroy methods, lazy/primary flags) — the builder just makes creating one ergonomic. **Starting points (static factories)** - `BeanDefinitionBuilder.genericBeanDefinition(Class)` — a `GenericBeanDefinition` for the class. - `genericBeanDefinition(Class, Supplier<T>)` — with an instance supplier (bridges to functional style). - `genericBeanDefinition(String className)` — by class name string. - `rootBeanDefinition(...)` / `childBeanDefinition(parentName)` — root or child definitions (parent/child inheritance). **Fluent configuration methods** - `addConstructorArgValue(Object)` / `addConstructorArgReference(String beanName)` — constructor injection by literal value or by reference to another bean. - `addPropertyValue(String, Object)` / `addPropertyReference(String, String)` — setter injection. - `setScope(String)`, `setLazyInit(boolean)`, `setPrimary(boolean)`, `setAutowireMode(int)`, `setInitMethodName(String)`, `setDestroyMethodName(String)`, `setFactoryMethod(String)`, `setRole(int)`. - `getBeanDefinition()` / `getRawBeanDefinition()` — obtain the built `AbstractBeanDefinition`. **Registering it** The built definition must be handed to a `BeanDefinitionRegistry`: ```java registry.registerBeanDefinition("myBean", builder.getBeanDefinition()); ``` Implementers of `BeanDefinitionRegistry` include `DefaultListableBeanFactory`, `GenericApplicationContext` (delegates to its factory), and `SimpleBeanDefinitionRegistry`. The registry is exactly what you receive in a `BeanFactoryPostProcessor#postProcessBeanFactory` (cast to registry) or a `BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry`. (Note: `ImportBeanDefinitionRegistrar` also receives a registry, but that annotation-driven path is a sibling topic.) **BeanDefinitionBuilder vs registerBean(Supplier)** - `registerBean(Class, Supplier, customizers)` is higher-level and lambda-first; the Supplier constructs the instance directly (no autowiring of constructor). - `BeanDefinitionBuilder` is lower-level and metadata-first; you can declare **constructor arg references / property references by bean name** and let the *container* resolve/wire them at creation. This is more expressive when you want container-managed wiring or property injection rather than a hand-built instance. **Gotchas** - You must register before `refresh()` (or from a post-processor that runs during context startup). - References (`addConstructorArgReference`) resolve at creation time — a missing target bean fails then. - `getBeanDefinition()` returns an `AbstractBeanDefinition`; cast if you need to add qualifiers. - Bean names are not auto-generated by the builder; you pass the name to `registerBeanDefinition` (or use `BeanDefinitionReaderUtils.registerWithGeneratedName`). **When to use** — dynamic registration inside a `BeanFactoryPostProcessor`, building definitions in a loop, or when you need setter/property wiring and named references that `registerBean`'s simple supplier form doesn't express.

  • How is BeanDefinitionBuilder different from registerBean(Class, Supplier)?
    The builder is metadata-first: you declare constructor/property references by name and the container wires and instantiates. registerBean's Supplier is instance-first: you build the object yourself with no constructor autowiring. Builder is more expressive for named references and setter injection.
  • Which interface do you need to actually register the built definition?
    BeanDefinitionRegistry — via registry.registerBeanDefinition(name, definition). GenericApplicationContext and DefaultListableBeanFactory implement it, and it's handed to BeanFactory/BeanDefinitionRegistry post-processors.

saying these in an interview costs you the question

  • Thinking BeanDefinitionBuilder itself registers the bean (it only builds the definition)
  • Confusing addConstructorArgValue (literal) with addConstructorArgReference (bean ref)
  • Assuming the builder auto-generates a bean name
  • Saying references resolve at build time rather than creation time

context