Interview Prep Zoneby CuriouserLabs

Question 3 of 5 · staff level

BeanFactoryPostProcessor vs BeanPostProcessor — what's the difference, and what real features are built on each?

🎤 Say this first

A BeanFactoryPostProcessor runs once, before any bean exists, and edits BeanDefinitions — property placeholder resolution and configuration-class parsing live here. A BeanPostProcessor intercepts every bean instance during creation with before/after-init hooks — @Autowired injection, @PostConstruct invocation, and AOP auto-proxying are all BPPs. One shapes the recipes; the other cooks and can even swap the dish (return a proxy instead of the bean).

The full picture

BeanFactoryPostProcessorBeanPostProcessor
RunsOnce, at startupAround every bean's initialization
Operates onBeanDefinitions (metadata)Bean instances
CanChange class, scope, properties; add definitions (BDRPP)Mutate the bean, or REPLACE it (proxy)
Powers${…} placeholders, @Configuration parsing, @ConfigurationProperties scanning@Autowired, @PostConstruct, @Transactional/@Async proxies, @Validated
A tiny BPP — the mechanism behind the magic
@Component
class TimedAnnotationProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String name) {
        if (!hasTimedMethods(bean.getClass())) return bean;
        return Proxy.newProxyInstance(               // return value REPLACES the bean
            bean.getClass().getClassLoader(),
            bean.getClass().getInterfaces(),
            (proxy, method, args) -> {
                long t0 = System.nanoTime();
                try { return method.invoke(bean, args); }
                finally { metrics.record(name, method, System.nanoTime() - t0); }
            });
    }
}

🔄 Likely follow-up questions

  • What is BeanDefinitionRegistryPostProcessor and who uses it? (@Configuration parsing itself)
  • Why should @Bean methods returning BFPPs be static?
  • How does Ordered/PriorityOrdered affect the post-processor pipeline?