What are merged bean definitions, and why does Spring compute a RootBeanDefinition per bean at runtime?
answer
- child + parent → one RootBeanDefinition
- getMergedBeanDefinition, cached + invalidated
- collections merge additively, scalars override
- abstract parent = template, never instantiated
- MergedBeanDefinitionPostProcessor caches @Autowired metadata
basics
~20 sA merged bean definition flattens a child definition together with its parent(s) into one complete RootBeanDefinition. Spring instantiates beans from this merged, fully-resolved copy so parent-inherited settings and defaults are all applied in one place.
solid answer
~40 sWhen a bean definition declares a `parentName`, its own metadata is partial — the rest is inherited from an (often `abstract`) parent template. Before creating the bean, the factory calls `getMergedBeanDefinition(name)`, which walks the parent chain and produces a self-contained `RootBeanDefinition` combining parent + child (child overrides parent for the same property, but constructor-arg and property-value collections are merged additively). The container always instantiates from this merged definition and caches it, invalidating the cache if a definition changes. `MergedBeanDefinitionPostProcessor` (e.g. `AutowiredAnnotationBeanPostProcessor`) gets a callback with the merged definition to detect injection metadata. This lets you factor shared config into abstract parents while keeping the runtime creation path simple and fully resolved.
code
java · 17 lines// Abstract parent template + child override
GenericBeanDefinition parent = new GenericBeanDefinition();
parent.setAbstract(true); // never instantiated directly
parent.getPropertyValues().add("timeout", 5000);
parent.getPropertyValues().add("pool", "default");
registry.registerBeanDefinition("baseClient", parent);
GenericBeanDefinition child = new GenericBeanDefinition();
child.setParentName("baseClient");
child.setBeanClass(HttpClient.class);
child.getPropertyValues().add("pool", "orders"); // overrides parent's "pool"
registry.registerBeanDefinition("ordersClient", child);
// At runtime the factory merges these:
BeanDefinition merged = ((ConfigurableListableBeanFactory) bf)
.getMergedBeanDefinition("ordersClient");
// merged is a RootBeanDefinition: class=HttpClient, timeout=5000, pool=orders, parentName=nullgo deeper
Know merging means combining parent and child config into one complete definition.
Explain parentName, abstract templates, and that the merged result is a RootBeanDefinition used for creation.
Detail scalar-override vs additive-collection-merge semantics, caching/invalidation, and getMergedBeanDefinition.
Discuss MergedBeanDefinitionPostProcessor as the injection-metadata seam, resolved runtime state on RootBeanDefinition, and copy semantics/thread-safety of merged caches.
## The problem it solves Spring supports **definition inheritance**: a child `BeanDefinition` names a `parentName`, and inherits the parent's class, scope, property values, etc., overriding selectively. The parent is frequently marked `abstract=true` — a pure template that is never instantiated itself. Inheritance is a DRY mechanism for sharing common configuration (e.g. shared connection settings across several beans). But a child definition is **incomplete** on its own. To create the bean, the container needs a single, fully-resolved view. ## Merging `AbstractBeanFactory.getMergedBeanDefinition(String beanName)` produces a **`RootBeanDefinition`** that flattens the entire parent chain into one self-contained definition: - Scalar settings (scope, lazy, class, init/destroy methods, autowire mode): child value wins if set, else inherited from parent. - **Collections merge additively**: `propertyValues` and `constructorArgumentValues` from the parent are combined with the child's; the child can add or override individual entries by name/index. (This is different from simple override — it's a union.) - The result is a `RootBeanDefinition` with `parentName == null` — nothing left to resolve. The factory **caches** merged definitions in `mergedBeanDefinitions` and clears the cache (`clearMergedBeanDefinition`) when a source definition is modified, so stale merges aren't used. ## Why RootBeanDefinition specifically `RootBeanDefinition` is the container's canonical *runtime* definition — it also carries resolved runtime state (target type, resolved constructor/factory method, post-processor flags). Creation logic (`createBean`, `doCreateBean`) only ever operates on a merged `RootBeanDefinition`, never on a raw child. This keeps the instantiation path uniform: whether a bean came from a standalone generic definition or a deep parent/child chain, `doCreateBean` sees the same complete object. ## MergedBeanDefinitionPostProcessor A special post-processor interface: `postProcessMergedBeanDefinition(RootBeanDefinition, Class, String)` is invoked right after merging and before injection. `AutowiredAnnotationBeanPostProcessor` and `CommonAnnotationBeanPostProcessor` use it to **scan and cache the injection metadata** (@Autowired/@Value/@Resource fields and methods) on the merged definition. This is a key extension seam. ## Gotchas - Abstract parents must be `abstract=true`; requesting `getBean` on an abstract definition throws `BeanIsAbstractException`. - Because collection values **merge**, forgetting that a parent already sets a property can lead to unexpected combined state — child doesn't fully replace a parent's list unless `merge` semantics are managed. - Merged definitions are **copies**: mutating one at runtime won't change the source child; and if the source changes, the cache is invalidated and re-merged. - Modifying the merged definition inside a post-processor is allowed and affects that bean's creation, but it's an infrastructure-level operation. ## When it matters in interviews Understanding merged definitions explains: why annotation injection metadata is discovered once and cached, why abstract parent templates work, and why the container never fails mid-creation due to unresolved parent references.
- What happens if you call getBean on an abstract parent definition?It throws BeanIsAbstractException. Abstract definitions are templates for inheritance only; they are never instantiated. You can still reference them as parentName from children.
- How do @Autowired injection points get discovered efficiently?Via a MergedBeanDefinitionPostProcessor (AutowiredAnnotationBeanPostProcessor) whose postProcessMergedBeanDefinition callback runs right after merging, scanning and caching the injection metadata on the merged RootBeanDefinition so it isn't re-parsed on every instantiation.
saying these in an interview costs you the question
- Saying the container instantiates beans directly from child definitions (it uses the merged RootBeanDefinition).
- Claiming a child fully replaces the parent's property list (collections merge additively unless managed).
- Thinking abstract parent beans are created but hidden (they are never instantiated).