Step‑by‑Step Walkthrough of Spring Boot’s Main‑Method to Container‑Ready Startup Chain
This article provides a line‑by‑line walkthrough of Spring Boot’s startup sequence, from the main method through SpringApplication.run, environment preparation, context creation, bean factory refresh, to the final ApplicationReadyEvent, highlighting key code snippets and common failure points.
1. Entry Phase: SpringApplication.run Initialization and Startup Preparation
1.1 Business Startup Entry Code
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// launch entry point
SpringApplication.run(DemoApplication.class, args);
}
}SpringApplication.run is a static utility method that performs two actions: instantiate a SpringApplication object and invoke run() to execute the complete startup process.
1.2 Core Source of SpringApplication Constructors
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// save primary sources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// deduce application type: NONE / SERVLET / REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// load ApplicationContextInitializer from spring.factories
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// load ApplicationListener from spring.factories
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// record the class containing the main method
this.mainApplicationClass = deduceMainApplicationClass();
}Instantiation performs three core actions:
Record the startup class as the root package for component scanning.
Automatically deduce the web environment to decide which context to create.
Read META-INF/spring.factories and cache global initializers and listeners, which underpin auto‑configuration and event listening.
1.3 run() Overall Scheduling Core Snippet
public ConfigurableApplicationContext run(String... args) {
// start timer
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 1. wrap command‑line arguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2. publish ApplicationStartingEvent
listeners.starting(applicationArguments);
try {
// 3. build environment object
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 4. create ApplicationContext
context = createApplicationContext();
// 5. pre‑process context
prepareContext(context, environment, listeners, applicationArguments);
// 6. core: refresh container
refreshContext(context);
// 7. post‑process
afterRefresh(context, applicationArguments);
stopWatch.stop();
// log startup time
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
// 8. execute Runner callbacks
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// startup exception handling
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// 9. publish ApplicationReadyEvent
listeners.ready(context, applicationArguments);
return context;
}The run() method orchestrates the entire startup chain, executing each stage in a fixed sequential order; each branch corresponds to an independent lifecycle event.
2. Environment Context Construction: Preparing Environment and Creating ApplicationContext
2.1 Core Logic of prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// create environment based on Web type
ConfigurableEnvironment environment = getOrCreateEnvironment();
// load command‑line args, system properties, configuration files
configureEnvironment(environment, applicationArguments.getSourceArgs());
// publish environment prepared event
listeners.environmentPrepared(environment);
// bind configuration to SpringApplication
bindToSpringApplication(environment);
return environment;
}Configuration priority: command‑line arguments > JVM arguments > system environment variables > application configuration files; later‑loaded values override earlier ones.
2.2 Creating the ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
// match context implementation based on Web type
switch (this.webApplicationType) {
case SERVLET:
contextClass = AnnotationConfigServletWebServerApplicationContext.class;
break;
case REACTIVE:
contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
break;
default:
contextClass = AnnotationConfigApplicationContext.class;
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}At this point only a new context object is created; no refresh, bean scanning, or bean instantiation occurs, but an internal DefaultListableBeanFactory is initialized.
2.3 Context Pre‑Preparation (prepareContext)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// bind environment to context
context.setEnvironment(environment);
// configure post‑processors and resource loaders
postProcessApplicationContext(context);
// apply all ApplicationContextInitializer
applyInitializers(context);
// publish context prepared event
listeners.contextPrepared(context);
// register the startup class as a configuration class
load(context, applicationArguments.getSourceArgs());
listeners.contextLoaded(context);
}The main step is registering the main startup class as a configuration source, which drives the subsequent @ComponentScan and @EnableAutoConfiguration processing.
3. Core Chain: refresh() Container Refresh Full Process (Bean Scanning, Creation)
3.1 refresh() Implementation (12 Fixed Steps)
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. pre‑refresh preparation
prepareRefresh();
// 2. obtain underlying BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. pre‑configure BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory post‑processor extension point
postProcessBeanFactory(beanFactory);
// 5. invoke all BeanFactoryPostProcessor (core of BeanDefinition scanning)
invokeBeanFactoryPostProcessors(beanFactory);
// 6. register BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. initialize message source
initMessageSource();
// 8. initialize event multicaster
initApplicationEventMulticaster();
// 9. Web container and custom special beans initialization
onRefresh();
// 10. register and broadcast early events
registerListeners();
// 11. core: instantiate all non‑lazy singleton beans
finishBeanFactoryInitialization(beanFactory);
// 12. finish refresh, publish ContextRefreshedEvent
finishRefresh();
} catch (BeansException ex) {
// exception handling logic
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}3.2 Key Node 5: invokeBeanFactoryPostProcessors (Component Scan Entry)
This method executes ConfigurationClassPostProcessor, which scans @ComponentScan, loads core auto‑configuration post‑processors, parses all business classes into BeanDefinition objects and registers them with the factory.
3.3 Key Node 11: finishBeanFactoryInitialization (Instantiate All Singleton Beans)
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// conversion service initialization omitted
// instantiate all singleton, non‑lazy beans
beanFactory.preInstantiateSingletons();
} preInstantiateSingletons()iterates over every BeanDefinition, performing instantiation, property population, initialization, and AOP proxy creation; all Controllers, Services, Repositories are fully created at this point.
3.4 Web Service Startup (onRefresh)
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to create Web server", ex);
}
}For a Servlet web context, this method creates and binds the embedded Tomcat/Jetty server to a port.
4. Startup Finalization: Container Ready Event and Runner Callbacks
4.1 Executing ApplicationRunner / CommandLineRunner
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// sort by Ordered
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
((ApplicationRunner) runner).run(args);
}
if (runner instanceof CommandLineRunner) {
((CommandLineRunner) runner).run(args.getSourceArgs());
}
}
}Cache warm‑up, dictionary loading, third‑party client pre‑connections are all placed here; at this moment all beans are fully instantiated and can be safely injected.
4.2 Application Ready Event
After all Runners finish, ApplicationReadyEvent is published, indicating that the project is fully started and ready to receive external HTTP requests.
5. High‑Frequency Failure Points in the Startup Chain
Custom listeners accessing beans cause NullPointerException during listeners.starting() because the context is not yet created.
Component scanning or auto‑configuration fails (e.g., wrong base package, missing @ComponentScan or failed @Conditional) during invokeBeanFactoryPostProcessors, leaving BeanDefinition unregistered.
Circular dependencies or empty injection errors arise in preInstantiateSingletons() when constructor cycles or scoped beans are injected into singletons.
Port conflict when the embedded Tomcat tries to bind in onRefresh(), resulting in a binding exception.
Runner execution errors in callRunners affect business warm‑up logic but do not block container startup.
6. Core Summary
The complete startup chain from the main method to a ready container is clearly layered and strictly sequenced into four phases: startup preparation, environment construction, container refresh, and finalization. The run() method coordinates the whole process, while refresh() is the IOC core handling bean scanning, definition registration, instantiation, and proxy generation. Understanding these native Spring Boot/Spring source snippets enables precise pinpointing of startup bottlenecks and the right moments for custom extensions.
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.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
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.
