How does ApplicationContextAwareProcessor work, and why do ApplicationContextAware-family callbacks fire at a different time than BeanNameAware?
answer
- ApplicationContextAwareProcessor = a BeanPostProcessor
- registered in prepareBeanFactory
- context-Aware fires in postProcessBeforeInit
- BeanNameAware earlier via invokeAwareMethods
- bare BeanFactory → context-Aware silently skipped
basics
~10 sApplicationContextAwareProcessor is a BeanPostProcessor the ApplicationContext registers. In its postProcessBeforeInitialization it calls the context-level Aware setters. BeanNameAware and BeanFactoryAware run earlier, directly in the bean factory's invokeAwareMethods.
solid answer
~40 sContext-level Aware interfaces (ApplicationContextAware, EnvironmentAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, EmbeddedValueResolverAware) are not understood by the bare BeanFactory. Instead, AbstractApplicationContext.prepareBeanFactory registers ApplicationContextAwareProcessor, a BeanPostProcessor. Its postProcessBeforeInitialization checks whether the bean implements any of those interfaces and calls the matching setter. Because this is a BPP callback, it runs during postProcessBeforeInitialization — later than BeanNameAware/BeanClassLoaderAware/BeanFactoryAware, which the factory invokes directly in invokeAwareMethods before any BPP runs. Consequently, in a plain DefaultListableBeanFactory with no ApplicationContext, ApplicationContextAware and friends silently never fire, while BeanNameAware still works. The processor invokes the setters in a fixed order: environment, embedded value resolver, resource loader, event publisher, message source, application startup, then application context.
code
java · 20 lines// Demonstrates the silent gotcha: a bare BeanFactory does NOT run
// ApplicationContextAwareProcessor, so context-level Aware never fires.
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.beans.factory.BeanNameAware;
class Probe implements BeanNameAware, ApplicationContextAware {
String name; ApplicationContext ctx;
public void setBeanName(String n) { this.name = n; }
public void setApplicationContext(ApplicationContext c) { this.ctx = c; }
}
var bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("probe",
new RootBeanDefinition(Probe.class));
Probe p = bf.getBean("probe", Probe.class);
// p.name == "probe" -> BeanNameAware ran (factory-level)
// p.ctx == null -> ApplicationContextAware did NOT run:
// no ApplicationContextAwareProcessor registered.go deeper
Just know a special processor injects the ApplicationContext; details are advanced.
Understand it is a BeanPostProcessor and runs during initialization.
Explain the factory-level vs BPP-level ordering split and the bare-BeanFactory gotcha.
Discuss the design rationale: keeping BeanFactory decoupled from ApplicationContext via the public BPP extension point.
## The mechanism A **`BeanPostProcessor` (BPP)** is a container extension point with two hooks, `postProcessBeforeInitialization` and `postProcessAfterInitialization`, invoked around every bean's init phase. `ApplicationContextAwareProcessor` is a BPP whose **only job** is to inject context-level infrastructure into beans that ask for it via Aware interfaces. When you build an `ApplicationContext`, `AbstractApplicationContext.refresh()` calls `prepareBeanFactory(beanFactory)`, which does two relevant things: 1. `beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))` — registers the processor. 2. `beanFactory.ignoreDependencyInterface(...)` for `EnvironmentAware`, `ResourceLoaderAware`, `ApplicationContextAware`, etc. — so autowiring by type does **not** try to satisfy these setters; the processor owns them. Inside `ApplicationContextAwareProcessor.postProcessBeforeInitialization(bean, name)`, it checks the bean's type and, if it implements any of the context-level Aware interfaces, calls the corresponding setter, then returns the (unchanged) bean. ## The ordering split — and why it matters Bean initialization in `AbstractAutowireCapableBeanFactory.initializeBean(...)` runs in this order: 1. `invokeAwareMethods(name, bean)` — handles **`BeanNameAware`, `BeanClassLoaderAware`, `BeanFactoryAware`** *directly*, no BPP involved. 2. `applyBeanPostProcessorsBeforeInitialization(...)` — runs every BPP's `postProcessBeforeInitialization`, **including `ApplicationContextAwareProcessor`** (which fires the context-level Aware setters) and the annotation processors that handle `@PostConstruct`. 3. `invokeInitMethods(...)` — `InitializingBean.afterPropertiesSet()` then the custom init-method. 4. `applyBeanPostProcessorsAfterInitialization(...)`. So **BeanNameAware/BeanFactoryAware are set strictly before ApplicationContextAware/EnvironmentAware/ResourceLoaderAware.** If a bean implements both, `setBeanName` is guaranteed to have run before `setApplicationContext`. ## Invocation order inside the processor Within `ApplicationContextAwareProcessor`, when a bean implements several context-level Aware interfaces, the setters are called in this fixed sequence: `EnvironmentAware` → `EmbeddedValueResolverAware` → `ResourceLoaderAware` → `ApplicationEventPublisherAware` → `MessageSourceAware` → `ApplicationStartupAware` → `ApplicationContextAware`. `ApplicationContextAware` is last. ## The big gotcha: bare BeanFactory Because the processor is registered only by `ApplicationContext`, if you construct a plain `DefaultListableBeanFactory` yourself and register beans, **`ApplicationContextAware`, `EnvironmentAware`, `ResourceLoaderAware`, etc. never fire** — silently, with no error. `BeanNameAware`/`BeanFactoryAware` still work because the factory invokes them itself. This surprises people writing tests or embedding a bare factory. ## Relationship to @PostConstruct Both `ApplicationContextAwareProcessor` and the annotation-based init processor (`CommonAnnotationBeanPostProcessor` / `InitDestroyAnnotationBeanPostProcessor`) run during `postProcessBeforeInitialization`. `ApplicationContextAwareProcessor` is registered very early in `prepareBeanFactory`, so it runs first: your Aware setters are populated **before** `@PostConstruct` executes. This is why it is safe to use an injected `ApplicationContext`/`Environment` inside a `@PostConstruct` method. ## Why this design Separating context-level Aware injection into a BPP keeps the low-level `BeanFactory` free of any dependency on the `ApplicationContext` abstraction — the factory does not even know what an `ApplicationContext` is. The context layers its richer capabilities in via the standard, public BPP extension point rather than special-casing the core engine. ## Term definitions - **`prepareBeanFactory`**: an early step of `AbstractApplicationContext.refresh()` that configures the factory (adds the aware processor, sets the class loader, registers resolvable dependencies). - **`invokeAwareMethods`**: internal factory method that directly calls the three factory-level Aware setters. - **`ignoreDependencyInterface`**: tells the autowiring engine to skip setters declared by that interface, since a BPP will fill them.
- Why does BeanNameAware work in a bare DefaultListableBeanFactory but ApplicationContextAware does not?BeanNameAware is invoked directly by the factory in invokeAwareMethods. ApplicationContextAware is injected by ApplicationContextAwareProcessor, a BeanPostProcessor only an ApplicationContext registers, so a bare factory never fires it.
- Can you rely on an injected ApplicationContext being available inside a @PostConstruct method?Yes. ApplicationContextAwareProcessor runs during postProcessBeforeInitialization and is registered early, before @PostConstruct executes, so the reference is set first.
- In what order are the setters called when a bean implements EnvironmentAware, ResourceLoaderAware, and ApplicationContextAware?EnvironmentAware first, then ResourceLoaderAware, then ApplicationContextAware last — the fixed order inside ApplicationContextAwareProcessor.