Why must BeanFactoryPostProcessor (and BeanPostProcessor) @Bean methods be declared static inside a @Configuration class?
answer
- post-processors created before config class enhanced
- non-static => config instantiated too early
- 'created too early' / 'cannot enhance' warning
- static = call method without instance
- static can't use @Value fields of the config
basics
~20 sBecause these post-processors are created very early, before the @Configuration class is processed. A static @Bean method can be called without instantiating the config class, so the container avoids creating it prematurely (which would trigger warnings and skip enhancement).
solid answer
~50 sBeanFactoryPostProcessors and BeanPostProcessors run at the very start of the container lifecycle — a BFPP's postProcessBeanFactory fires before most other beans are instantiated. If you define one as a non-static @Bean method on a @Configuration class, Spring must instantiate that configuration class to invoke the method. But the config class is being instantiated *too early*, before it can be fully processed/CGLIB-enhanced. Spring logs a warning like 'Cannot enhance @Configuration bean definition ... its singleton instance is created too early', and any @Autowired/@Value inside that config class may not be honored — you lose the @Configuration proxying that makes inter-@Bean calls return singletons. Declaring the method static breaks the dependency: Spring can call the static factory method without creating the enclosing config instance, so the post-processor is produced cleanly and the config class is enhanced normally.
code
java · 22 lines@Configuration
public class InfraConfig {
// WRONG: non-static forces InfraConfig to be instantiated too early,
// before it can be CGLIB-enhanced -> 'created too early' warning.
// @Bean
// public BeanFactoryPostProcessor bfpp() { return new MyBfpp(); }
// RIGHT: static -> invoked without an InfraConfig instance.
@Bean
public static BeanFactoryPostProcessor bfpp() {
return new MyBfpp();
}
// If the post-processor needs config, inject via method params,
// NOT via @Autowired fields on this class (static has no instance).
@Bean
public static BeanFactoryPostProcessor configurableBfpp(
@Value("${infra.flag:false}") boolean flag) {
return new MyBfpp(flag);
}
}go deeper
Just remember: post-processor @Bean methods should be static.
Explain the early-instantiation timing that motivates static.
Articulate the CGLIB enhancement loss, the 'created too early' warning, and the static trade-off (no instance fields).
Relate to proxyBeanMethods modes, ConfigurationClassPostProcessor ordering, and how framework/auto-config code exposes post-processors statically.
## The rule When you register a `BeanFactoryPostProcessor` (BFPP) or `BeanPostProcessor` (BPP) via a `@Bean` method inside a `@Configuration` class, that method **should be `static`**: ```java @Configuration public class InfraConfig { @Bean public static BeanFactoryPostProcessor myBfpp() { ... } // static } ``` ## Why — the timing conflict Spring's `refresh()` processes post-processors **before** ordinary singletons: 1. `invokeBeanFactoryPostProcessors()` — BFPPs must already exist to run here. 2. `registerBeanPostProcessors()` — BPPs must be instantiated here. 3. only later are regular singletons created. To *produce* a BFPP/BPP that lives on a config class, Spring must call your `@Bean` method. If the method is an **instance** method, calling it requires an **instance of the @Configuration class**. So Spring is forced to instantiate `InfraConfig` at step 1/2 — far earlier than normal beans. The problem: `@Configuration` classes are normally **CGLIB-enhanced** — Spring subclasses them so that inter-`@Bean` method calls are intercepted and return the shared singleton ("full" configuration mode). That enhancement is applied by `ConfigurationClassPostProcessor` (itself a `BeanDefinitionRegistryPostProcessor`) and by proxy-creating BPPs. If the config class is instantiated **before** those processors have done their work, it is created as a **raw, un-enhanced** instance. Spring detects this and logs: ``` Cannot enhance @Configuration bean definition 'infraConfig' since its singleton instance has been created too early. ``` Consequences of an un-enhanced config instance created too early: - Inter-`@Bean` method calls inside it may create **new** objects instead of returning singletons. - `@Autowired`/`@Value` fields on that config class may be **unresolved**, because the BPPs that inject them aren't registered yet. ## Why static fixes it A `static` `@Bean` method is invoked on the class, **not** on an instance. Spring can call `InfraConfig.myBfpp()` without ever constructing `InfraConfig`. So: - The post-processor is produced at the right early moment. - The `@Configuration` class instance is NOT created prematurely, so it is enhanced normally later. Trade-off: a `static` method **cannot use** the config class's `@Autowired`/`@Value` instance fields (there is no instance). If your post-processor needs configuration, pass it via method parameters (Spring injects `@Bean` method parameters) or read the `Environment`/`BeanFactory` inside the post-processor itself. ## Does this apply everywhere? - **@Configuration (proxyBeanMethods=true, the default):** yes, the static rule matters most here because enhancement is at stake. - **@Configuration(proxyBeanMethods=false) / lite mode / plain @Component:** less severe, but the early-instantiation timing issue and the Spring warning can still appear, so static is still the safe, recommended practice. - Framework code follows this too — e.g. the auto-configured `PropertySourcesPlaceholderConfigurer` is exposed via a static method. ## How you notice it in the wild The tell-tale sign is a startup WARN log about a config bean's instance being created too early / not being eligible for enhancement, often accompanied by mysteriously un-injected fields in that config class or duplicate beans. The fix is almost always: add `static` to the post-processor's `@Bean` method.
- What warning does Spring log when you get this wrong, and what breaks?Something like 'Cannot enhance @Configuration bean definition ... its singleton instance is created too early'. The config class is created un-enhanced, so inter-@Bean calls may not return singletons and @Autowired/@Value on that class may be unresolved.
- A downside of static @Bean methods is that they can't access instance @Autowired/@Value fields. How do you pass configuration to the post-processor then?Declare parameters on the static @Bean method — Spring resolves and injects them (including @Value on the parameter) — or read the Environment/BeanFactory from inside the post-processor's own methods.
saying these in an interview costs you the question
- Saying static is only a style preference with no functional effect.
- Claiming a non-static post-processor bean simply won't be created.
- Not knowing it stems from post-processors running before config enhancement.
- Thinking @Lazy or @DependsOn is the correct fix.