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.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Step‑by‑Step Walkthrough of Spring Boot’s Main‑Method to Container‑Ready Startup Chain

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaspringbootDependencyInjectionStartupApplicationContextWebServerSpringFrameworkBeanLifecycle
Java Tech Workshop
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.