Deep Dive into Spring Boot IoC Container Initialization (Part 4)
This article breaks down Spring Boot's core refresh() method into twelve detailed steps, explaining how the IoC container prepares, configures, and starts up—including bean factory preparation, post‑processor registration, singleton instantiation, and final event publishing—providing interview‑ready insights and code illustrations.
In the fourth installment of the Spring Boot source‑code series, we explore the most critical and interview‑frequent part of the framework: the IoC container initialization performed inside the refresh() method of AbstractApplicationContext. The method consists of twelve sequential steps that together bootstrap the Spring application context.
1. refresh() Overview
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. Prepare for refresh
prepareRefresh();
// 2. Obtain BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. Prepare BeanFactory (class loader, expression resolver, etc.)
prepareBeanFactory(beanFactory);
// 4. Subclass extensions (Web container hooks)
postProcessBeanFactory(beanFactory);
// 5. Execute BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors(beanFactory);
// 6. Register BeanPostProcessors
registerBeanPostProcessors(beanFactory);
// 7. Initialize MessageSource (i18n)
initMessageSource();
// 8. Initialize ApplicationEventMulticaster
initApplicationEventMulticaster();
// 9. Subclass onRefresh (e.g., start Tomcat)
onRefresh();
// 10. Register listeners
registerListeners();
// 11. Instantiate all non‑lazy singleton beans (core step)
finishBeanFactoryInitialization(beanFactory);
// 12. Finish refresh and publish ContextRefreshedEvent
finishRefresh();
}
}2. Detailed Step‑by‑Step Breakdown
Step 1 – prepareRefresh()
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
initPropertySources(); // hook for subclasses
getEnvironment().validateRequiredProperties();
this.earlyApplicationEvents = new LinkedHashSet<>();
}Purpose: Set the container’s startup timestamp, mark it as active, initialize property sources, validate required environment properties, and create a collection for early events.
Step 2 – obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// Refresh BeanFactory (implemented by subclasses)
refreshBeanFactory();
// Return the refreshed BeanFactory
return getBeanFactory();
}Purpose: Refresh or create the underlying BeanFactory (typically a DefaultListableBeanFactory).
Step 3 – prepareBeanFactory(beanFactory)
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(...));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(...));
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(...));
}
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// ... register systemProperties, systemEnvironment, etc.
}Purpose: Configure the BeanFactory’s class loader, expression resolver, property editors, special *Aware interfaces, resolvable dependencies, and early post‑processors.
Step 4 – postProcessBeanFactory(beanFactory)
This method is empty in the base class and is overridden by subclasses (e.g., AnnotationConfigServletWebServerApplicationContext) to add Web‑specific processing.
Step 5 – invokeBeanFactoryPostProcessors(beanFactory)
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(
beanFactory, getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(...));
}
}Core logic: Executes all BeanFactoryPostProcessor implementations. The order is:
First, BeanDefinitionRegistryPostProcessor (PriorityOrdered → Ordered → others)
Then, regular BeanFactoryPostProcessor (PriorityOrdered → Ordered → others)
Typical annotations such as @ComponentScan, @Import, and @Bean are parsed during this step.
Step 6 – registerBeanPostProcessors(beanFactory)
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}Purpose: Registers all BeanPostProcessor implementations, which will intercept bean creation before and after instantiation. Registration order:
All PriorityOrdered processors
All Ordered processors
All remaining processors
Special MergedBeanDefinitionPostProcessor handling
Common processors include: AutowiredAnnotationBeanPostProcessor – handles
@Autowired CommonAnnotationBeanPostProcessor– handles @Resource,
@PostConstruct ApplicationContextAwareProcessor– processes all *Aware interfaces
Step 7 – initMessageSource()
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
} else {
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
}
}Purpose: Sets up the internationalization message source, either using a custom bean or a default delegating implementation.
Step 8 – initApplicationEventMulticaster()
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
}
}Purpose: Initializes the event broadcasting mechanism used by Spring’s ApplicationEvent system.
Step 9 – onRefresh()
The base implementation is empty; subclasses such as ServletWebServerApplicationContext override it to start the embedded web server (Tomcat, Jetty, etc.).
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer(); // starts Tomcat for a servlet context
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
// Tomcat is started here
}Step 10 – registerListeners()
protected void registerListeners() {
// Register statically specified listeners
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Register all beans of type ApplicationListener
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Process early events published before refresh
Set<ApplicationEvent> earlyEvents = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEvents != null) {
for (ApplicationEvent earlyEvent : earlyEvents) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}Step 11 – finishBeanFactoryInitialization(beanFactory)
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 1. Initialize ConversionService if present
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 2. Register default embedded value resolver
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 3. Initialize LoadTimeWeaver aware beans
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 4. Clear temporary class loader
beanFactory.setTempClassLoader(null);
// 5. Freeze configuration (no further bean definition changes)
beanFactory.freezeConfiguration();
// 6. Instantiate all non‑lazy singleton beans (core)
beanFactory.preInstantiateSingletons();
// 7. Trigger SmartInitializingSingleton callbacks
for (String beanName : beanFactory.getBeanDefinitionNames()) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
((SmartInitializingSingleton) singletonInstance).afterSingletonsInstantiated();
}
}
}The preInstantiateSingletons() method iterates over all bean definitions, skips abstract, lazy, or prototype beans, and calls getBean() to create each singleton. FactoryBeans are handled specially, and after creation, any SmartInitializingSingleton receives a callback.
Step 12 – finishRefresh()
protected void finishRefresh() {
// 1. Clear ResourceLoader caches
clearResourceCaches();
// 2. Initialize lifecycle processor
initLifecycleProcessor();
// 3. Invoke onRefresh on the lifecycle processor
getLifecycleProcessor().onRefresh();
// 4. Publish ContextRefreshedEvent
publishEvent(new ContextRefreshedEvent(this));
// 5. Register MBean for JMX monitoring
LiveBeansView.registerApplicationContext(this);
}This finalizes the context startup, making the application fully operational.
Interview Answer Template
Spring container startup centers on the refresh() method, which consists of twelve steps: prepareRefresh : set container state and initialize environment. obtainFreshBeanFactory : acquire or create the BeanFactory. prepareBeanFactory : configure class loader, expression resolver, etc. postProcessBeanFactory : subclass extensions (Web container adds its own processing). invokeBeanFactoryPostProcessors : execute BeanFactoryPostProcessors; parses @ComponentScan , @Bean , etc. registerBeanPostProcessors : register processors such as @Autowired handling. initMessageSource : set up internationalization. initApplicationEventMulticaster : set up event broadcasting. onRefresh : subclass hook where the embedded Tomcat is started. registerListeners : register static and bean‑defined listeners. finishBeanFactoryInitialization : instantiate all non‑lazy singleton beans – the core step. finishRefresh : clear caches, invoke lifecycle callbacks, and publish ContextRefreshedEvent . Step 5 parses bean definitions, step 6 registers the processors, and step 11 actually creates the beans.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Coder Trainee
Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
