How does BeanDefinitionRegistryPostProcessor differ from BeanFactoryPostProcessor, and in what order do their methods run?
answer
- BDRPP extends BFPP
- registry method = add new definitions
- all registry methods run BEFORE all factory methods
- ConfigurationClassPostProcessor is a BDRPP
- MapperScannerConfigurer registers beans dynamically
basics
~10 sBeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor and adds a method to register brand-new bean definitions. Its registry method runs first (for all such processors), then all the postProcessBeanFactory methods run.
solid answer
~40 sBeanDefinitionRegistryPostProcessor (BDRPP) is a sub-interface of BeanFactoryPostProcessor. A plain BFPP can only *mutate* existing bean definitions via postProcessBeanFactory. A BDRPP additionally gets postProcessBeanDefinitionRegistry(BeanDefinitionRegistry), which hands you the mutable registry so you can *add* or remove definitions dynamically. Spring runs all postProcessBeanDefinitionRegistry methods first (ordered PriorityOrdered, Ordered, then the rest), so newly registered definitions become visible to later processors; only afterwards does it run every postProcessBeanFactory. The most important BDRPP is ConfigurationClassPostProcessor — it parses @Configuration/@ComponentScan/@Import and registers the resulting definitions. MyBatis's MapperScannerConfigurer and similar 'scan and register beans' tools also use this hook. Because BDRPP runs earliest, it is the correct place to programmatically contribute beans that must exist before normal processing.
code
java · 20 linesimport org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.*;
public class DynamicBeanRegistrar implements BeanDefinitionRegistryPostProcessor {
// Runs FIRST: add brand-new definitions the container didn't know about.
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinition def = BeanDefinitionBuilder
.genericBeanDefinition(AuditLogger.class)
.getBeanDefinition();
registry.registerBeanDefinition("auditLogger", def);
}
// Runs LATER: mutate existing definitions (including the one added above).
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
bf.getBeanDefinition("auditLogger").setLazyInit(true);
}
}go deeper
Know BDRPP can add new bean definitions and extends BFPP.
State the two methods and that the registry method runs first.
Explain the full ordering (all registry methods, then all factory methods; PriorityOrdered/Ordered sub-order) and cite ConfigurationClassPostProcessor.
Discuss dynamic bean registration patterns (Spring Data/MyBatis registrars), ImportBeanDefinitionRegistrar vs BDRPP, and re-scanning for newly added processors.
## The two interfaces ```java public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory bf); } public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry); } ``` - **BeanFactoryPostProcessor (BFPP)** — `postProcessBeanFactory` receives the `ConfigurableListableBeanFactory`. You can read and **modify** existing definitions, but the API is not intended for freely registering large numbers of new ones. Adding definitions here is too late for some processing. - **BeanDefinitionRegistryPostProcessor (BDRPP)** — extends BFPP, so it has *both* methods. The extra `postProcessBeanDefinitionRegistry` gives you the `BeanDefinitionRegistry`, whose whole purpose is `registerBeanDefinition` / `removeBeanDefinition`. This is the sanctioned place to **add new bean definitions programmatically**. ## Execution order (important) Inside `PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors`, Spring processes them in a specific sequence: 1. **All `postProcessBeanDefinitionRegistry` methods** of every BDRPP run first. Within that, sub-ordering is: `PriorityOrdered` first, then `Ordered`, then the rest. Crucially, after each batch registers new definitions, Spring re-scans for any newly-added BDRPPs and runs them too — this is how `ConfigurationClassPostProcessor` (which registers your @Configuration-derived definitions) can bring in further processors. 2. **Then** the `postProcessBeanFactory` method of every BDRPP runs. 3. **Then** the `postProcessBeanFactory` of every plain BFPP runs (again PriorityOrdered -> Ordered -> rest). Key takeaway: **all registry post-processing finishes before any factory post-processing begins.** So definitions you add in `postProcessBeanDefinitionRegistry` are fully visible to every later `postProcessBeanFactory` (and, of course, to instantiation). ## The flagship example: ConfigurationClassPostProcessor This internal BDRPP is what makes annotation-based config work. In its `postProcessBeanDefinitionRegistry` it parses `@Configuration`, `@ComponentScan`, `@Import`, `@Bean`, `@Conditional`, etc., and registers a bean definition for each discovered bean. In its `postProcessBeanFactory` it applies the CGLIB **enhancement** to full `@Configuration` classes (so inter-`@Bean` calls return singletons). It runs as a `PriorityOrdered` BDRPP so it goes first. ## When to write your own BDRPP - You need to **register beans dynamically** whose number/names aren't known at compile time — e.g. one repository/proxy bean per interface found on the classpath. `MapperScannerConfigurer` (MyBatis) and Spring Data's registrars work this way. - You are integrating a library that must contribute beans very early, before other processing. Minimal example that registers a bean: ```java public class RegistrarBfpp implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { var def = BeanDefinitionBuilder .genericBeanDefinition(MetricsCollector.class) .setScope(BeanDefinition.SCOPE_SINGLETON) .getBeanDefinition(); registry.registerBeanDefinition("metricsCollector", def); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) { // optional: tweak existing definitions after registration } } ``` ## Gotchas - Register it correctly: like any BFPP, a BDRPP declared via `@Bean` on a `@Configuration` class should be **static** (same early-instantiation reason as other post-processors). - Don't instantiate beans from either method — same proxy-skipping hazard as any BFPP. - `ImportBeanDefinitionRegistrar` is a related, higher-level mechanism (driven by `@Import`) for registering definitions declaratively; a BDRPP is the lower-level, always-runs hook.
- Name a well-known BeanDefinitionRegistryPostProcessor and what it does.ConfigurationClassPostProcessor — parses @Configuration/@ComponentScan/@Import and registers the discovered bean definitions, then CGLIB-enhances full @Configuration classes. MyBatis's MapperScannerConfigurer is another, registering a proxy bean per mapper interface.
- If a plain BFPP tried to registerBeanDefinition in postProcessBeanFactory, why is a BDRPP preferred?postProcessBeanFactory runs after all registry post-processing, so beans added there miss the registry phase and some processing; the registry method exists precisely to add definitions at the right, earlier moment where they're fully picked up.
saying these in an interview costs you the question
- Saying BFPP can register new beans just as well (it can mutate but the registry hook is the proper place).
- Getting the order backwards (factory methods before registry methods).
- Not knowing ConfigurationClassPostProcessor is a BDRPP.
- Forgetting the @Bean method should be static.