How a Spring Boot Web App Starts: Step-by-Step Explained
This article explains the full startup process of a Spring Boot Web application, from the main() method to the application being fully initialized. I chose the older version(Spring Boot 1.5.7.RELEASE) because it is more understandable and fundational. And I will provide some updates in Spring Boot 3.x.
Step 0: App Entry Point (main method)
Everything starts from this main() method.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Step 1: Initializing SpringApplication
SpringApplication.run() creates a SpringApplication object and calls its run() method. Inside the constructor, initialize() prepares the setup.
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public SpringApplication(Object... sources) {
initialize(sources);
}
Key Actions in initialize()
setInitializers(): Adds setup logic before context refresh.setListeners(): Registers listeners for app events.- (Updated in Spring Boot 3.x): Replaced
webEnvironmentwithWebApplicationTypeenum (SERVLET, REACTIVE, NONE).private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
Step 2: Running the App
The code is a simplified version that retains the key parts, labeled from Step 3 to Step 9.
public ConfigurableApplicationContext run(String... args) {
listeners.starting();
// Step 3: Parse application arguments and prepare the environment
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
ConfigurableEnvironment env = prepareEnvironment(listeners, appArgs);
// Step 4: Print the startup banner
Banner banner = printBanner(env);
// Step 5: Create the application context
context = createApplicationContext();
// Step 6: Prepare the context before refreshing
prepareContext(context, env, listeners, appArgs, banner);
// Step 7: Refresh the context (core Spring logic)
refreshContext(context);
// Step 8: Call custom runners after context initialization
afterRefresh(context, appArgs);
// Step 9: Notify listeners that startup is finished
listeners.finished(context, null);
return context;
}
Step 3: Processing Command-Line Args and Environment
ApplicationArguments: Parses command-line input.ConfigurableEnvironment: Handles environment config.
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
ConfigurableEnvironment env = prepareEnvironment(listeners, appArgs);
(Updated): Spring Boot 3 uses SpringApplicationBuilder more for configuration.
Step 4: Showing the Banner
Displays the startup banner.
Banner banner = printBanner(env);
Customize it with.
banner.location=classpath:my-banner.txt
Step 5: Creating the Context
Spring Boot creates a context (container), usually a web context.
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
contextClass = Class.forName(
this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
(Updated): New default is AnnotationConfigServletWebServerApplicationContext.
Step 6: Setting Up the Context
Before refreshing, it injects core values and beans.
prepareContext(context, env, listeners, appArgs, banner);
Tasks:
- Apply initializers
- Register
ApplicationArgumentsand others as beans - Load source classes
Step 7: Refreshing the Context
This is where Spring starts creating beans and composing everything.
refreshContext(context);
((AbstractApplicationContext) context).refresh();
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// Convert annotated beans into BeanDefinition structures
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// Register bean post-processors
// AOP is implemented using the registered AnnotationAwareAspectJAutoProxyCreator
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// Create singleton beans
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
What happens during refresh():
prepareRefresh()prepares the context for refresh.obtainFreshBeanFactory()creates or refreshs the internalBeanFactory.prepareBeanFactory(beanFactory)configures theBeanFactorybefore it is used.postProcessBeanFactory(beanFactory)is an extension point for subclasses.invokeBeanFactoryPostProcessors(beanFactory)converts annotated components (@Component, etc.) into BeanDefinitions.registerBeanPostProcessors(beanFactory)registers processors that intervene in bean creation.initMessageSource()sets up internationalization (i18n) message handling.initApplicationEventMulticaster()initializes the event publishing mechanism.onRefresh()is an extension hook for subclasses (like web apps).registerListeners()registers application event listeners.finishBeanFactoryInitialization(beanFactory)creates all singleton beans.finishRefresh()publish theContextRefreshedEvent.
Step 8: Running Custom Code
Spring Boot checks for ApplicationRunner or CommandLineRunner beans and runs them.
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
Step 9: Final Events
At the end, it sends either:
ApplicationReadyEventif startup was successful- or
ApplicationFailedEventif something broke
listeners.finished(context, null);
Custom listener example
@Component
public class SloganReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//Here, you can do what you want to do.
}
}
(Updated): Prefer @EventListener annotation:
@EventListener
public void onReady(ApplicationReadyEvent event) {
// Custom logic here
}
Step 10: App Is Running
So far, the app is running, with all beans ready and server listening for requests.
Simplified Diagram
main() → SpringApplication.run()
→ initialize()
→ run()
→ prepareEnvironment()
→ printBanner()
→ createApplicationContext()
→ prepareContext()
→ refreshContext() → refresh()
→ afterRefresh() → callRunners()
→ listeners.finished()
Key Changes from Spring Boot 1.5 to 3.x
| Feature | Spring Boot 1.5 | Spring Boot 3.x (Updated) |
|---|---|---|
| Web Context Class | AnnotationConfigEmbeddedWebApplicationContext |
AnnotationConfigServletWebServerApplicationContext |
| Web Env Detection | webEnvironment boolean |
WebApplicationType enum |
| Event Listening | Implements ApplicationListener |
Use @EventListener |
| Banner Support | banner.txt, images |
Unicode + styled banner support |