Spring Boot Startup Process
Spring Boot startup involves: loading config, creating the ApplicationContext, scanning and registering beans, and running Runners. This article explains the flow, key extension points, and how to diagnose slow startup with a reference table.
Overview
- Entry point:
SpringApplication.run(). Processes@SpringBootApplication(which includes@Configuration,@ComponentScan,@EnableAutoConfiguration). - Context: Creates the appropriate
ApplicationContext(e.g.ServletWebServerApplicationContextfor web apps). Loads bean definitions, instantiates beans, injects dependencies, and runs initialization callbacks. - Auto-configuration:
@EnableAutoConfigurationloads auto-configuration classes based on the classpath and@ConditionalOnXxxconditions (e.g.DataSourceAutoConfiguration,WebMvcAutoConfiguration). - Runners:
ApplicationRunnerandCommandLineRunnerbeans run after the context is fully initialized; used for startup tasks, warmup, etc.
Example
Example 1: Startup flow overview
Plain textrun() → create ApplicationContext → load bean definitions (component scan, @Import, auto-config) → refresh(): instantiate, inject, run init callbacks → start embedded web server (if applicable) → run ApplicationRunner and CommandLineRunner beans
- The
refresh()phase is where most of the work happens: bean creation,@PostConstruct,BeanPostProcessor, AOP proxy creation, etc.
Example 2: Extension points
| Extension | When it runs |
|---|---|
| ApplicationContextInitializer | Before context refresh |
| BeanPostProcessor | Before/after each bean is created |
| ApplicationRunner / CommandLineRunner | After context is ready |
| @PostConstruct | After bean properties are injected |
Example 3: Auto-configuration in action
- With
tomcatandspring-webmvcon the classpath: auto-configures the embedded Tomcat andDispatcherServlet. - With
spring-boot-starter-data-jpaand aDataSourcebean or config: auto-configures JPA and the data source. - You can exclude auto-configurations with
@SpringBootApplication(exclude = {...})or override with your own@Beandefinitions.
Example 4: ApplicationRunner
Java@Component public class MyRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // Runs after the context is fully ready // args.getNonOptionArgs(), args.getOptionNames(), etc. } }
- Use for startup tasks that need the full context (e.g. preloading caches, registering with a service registry).
- Avoid long-running or blocking work; use
@Asyncor a separate thread pool if needed.
Example 5: CommandLineRunner
Java@Component public class CliRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // Same idea as ApplicationRunner but with raw String[] args } }
- Simpler than
ApplicationRunnerwhen you only need raw args. Both run after the context is ready.
Example 6: BeanPostProcessor for custom init
Java@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // Called after @PostConstruct, InitializingBean, init-method return bean; } }
- Use when you need to process every bean (e.g. for logging, validation, or custom metadata).
Core Mechanism / Behavior
- @SpringBootApplication: Combines
@Configuration,@ComponentScan(on the package of the annotated class and below), and@EnableAutoConfiguration. - Conditional loading:
@ConditionalOnClass,@ConditionalOnProperty,@ConditionalOnBean, etc. control when auto-configurations apply. This keeps the context lean when dependencies are absent. - Refresh phase:
AbstractApplicationContext.refresh()loads definitions, creates singletons, runs post-processors, and starts the web server. This is where startup time is spent.
Diagnosing Slow Startup
- Too many beans: Reduce component scan scope; avoid unnecessary
@Componenton large packages. - Heavy @PostConstruct: Move blocking or slow logic to Runners, async init, or lazy beans.
- Database connection pool: Pool init can be slow; consider lazy init or smaller initial size.
- Auto-configuration: Use
debug=trueor actuator to see which auto-configurations are applied; exclude unused ones.
Key Rules
- Slow startup: Check bean count, auto-config, blocking logic in
@PostConstruct, DB pool init, and other heavy init. - Conditional annotations drive auto-config; understand
@ConditionalOnXxxwhen a feature is unexpectedly enabled or disabled. - Avoid long blocking work in Runners; use async or lazy init for expensive operations.
- Use
spring.main.lazy-initialization=truecautiously; it defers bean creation but can hide startup issues and shift cost to first request.
What's Next
See IOC & Bean Lifecycle, Config Priority. See AOP for bean proxy creation during startup.