What actually happens during context refresh
The twelve phases between SpringApplication.run and a context that is ready to serve.
Open this lesson in the learning hubKey points
- Bean creation is not the first step. The container first builds bean definitions - metadata about what could be created - and only later instantiates them.
- A
BeanFactoryPostProcessorruns against those definitions, before any bean object exists. That is how@Valueplaceholders are resolved and how a definition can be modified or removed. - A
BeanPostProcessorruns against each finished bean instance, twice: before and after initialisation callbacks. AOP proxies are created in the second hook. - This ordering explains a class of confusing failure: injecting a normal bean into a
BeanFactoryPostProcessorforces that bean to be created far too early, before post-processors that would have configured it have run. - Singletons are instantiated eagerly at the end of refresh, which is why a wiring mistake fails at startup rather than on the first request. That is a feature.
- Only after all of that does
finishRefreshpublishContextRefreshedEventand startSmartLifecyclebeans - the web server among them.
Example
// Runs BEFORE any bean is instantiated - it edits definitions, not objects.
@Component
class AuditDefaultsPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) {
for (String name : factory.getBeanDefinitionNames()) {
BeanDefinition def = factory.getBeanDefinition(name);
// Safe: we are reading metadata, not calling getBean().
if (def.getBeanClassName() != null && def.getBeanClassName().endsWith("Repository")) {
def.setLazyInit(true);
}
}
}
}
// Runs against each finished bean. The SECOND callback is where proxies appear,
// so a bean injected here may not yet be the proxy other beans will receive.
@Component
class TimingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean; // before @PostConstruct / afterPropertiesSet
}
@Override
public Object postProcessAfterInitialization(Object bean, String name) {
return bean; // after init - AOP wraps the bean at this point
}
}
// Ordered startup work. Phase decides who starts first and who stops last.
@Component
class WarmupLifecycle implements SmartLifecycle {
private volatile boolean running;
@Override public int getPhase() { return Integer.MIN_VALUE + 10; } // very early
@Override public void start() { running = true; }
@Override public void stop() { running = false; }
@Override public boolean isRunning() { return running; }
}
Definitions are built first, then post-processed, then instantiated, then proxied - and every ordering bug you meet is a consequence of that sequence.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.