How SpringBootApplication works
Inspecting the definition of @SpringBootApplication reveals that it is composed of three foundational Spring annotations:
🟢 Junior Level
@SpringBootApplication is the primary composite meta-annotation that bootstraps any Spring Boot application. It is placed on the main entry-point class containing the public static void main(String[] args) method.
package com.example.shop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ShopApplication {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
}
The 3 Core Meta-Annotations Inside @SpringBootApplication
Inspecting the definition of @SpringBootApplication reveals that it is composed of three foundational Spring annotations:
[@SpringBootApplication]
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
[@SpringBootConfiguration] [@EnableAutoConfiguration] [@ComponentScan]
Declares class as a Enables automatic bean Scans current package &
Spring Configuration discovery based on all subpackages for
source & test anchor. classpath JARs. @Component / @Service.
@SpringBootConfiguration(A specialized@Configuration):- Designates the class as a configuration source capable of declaring
@Beanfactory methods. - Acts as a unique marker for Spring Boot testing slices (
@SpringBootTest,@WebMvcTest,@DataJpaTest) to automatically discover the root application configuration.
- Designates the class as a configuration source capable of declaring
@EnableAutoConfiguration:- Enables Spring Boot’s auto-configuration engine, which scans classpath libraries and configuration properties to automatically register pre-configured infrastructure beans (databases, security, web servers, Jackson).
@ComponentScan:- Recursively scans the package of the declaring class and all its child subpackages for classes annotated with
@Component,@Service,@Repository, and@Controller.
- Recursively scans the package of the declaring class and all its child subpackages for classes annotated with
[!IMPORTANT] Root Package Placement Rule: Always place your
@SpringBootApplicationclass in the root package of your application (e.g.,com.example.shop). Because@ComponentScandefaults to scanning the declaring class’s package and subpackages, all controllers, services, and repositories located below the root will be discovered automatically.
🟡 Middle Level
Configuration Attributes of @SpringBootApplication
@SpringBootApplication acts as a facade, exposing aliases that forward attributes to its underlying composite annotations:
@SpringBootApplication(
// Forwarded to @EnableAutoConfiguration: Excludes specific auto-configurations
exclude = { DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class },
// Forwarded to @ComponentScan: Overrides default package scanning
scanBasePackages = { "com.example.shop", "com.example.common" },
// Type-safe package scanning using marker classes (refactoring safe)
scanBasePackageClasses = { CommonMarker.class },
// Forwarded to @SpringBootConfiguration: Toggles CGLIB proxying (Full vs Lite mode)
proxyBeanMethods = true
)
public class ShopApplication { }
The SpringApplication.run() Execution Pipeline
When SpringApplication.run(ShopApplication.class, args) is invoked, Spring Boot coordinates a deterministic sequence of bootstrap steps:
1. Determine Application Type (SERVLET, REACTIVE, or NONE)
│
▼
2. Prepare Environment (Loads application.yml, CLI arguments, OS environment variables, Active Profiles)
│
▼
3. Instantiate ApplicationContext (e.g. AnnotationConfigServletWebServerApplicationContext)
│
▼
4. Prepare Context (Applies ApplicationContextInitializers, registers main Application.class)
│
▼
5. context.refresh() (Core Spring Engine: ComponentScan, BeanFactoryPostProcessors, onRefresh Web Server, Singletons)
│
▼
6. Execute Runners (Invokes all CommandLineRunner and ApplicationRunner beans)
│
▼
7. Publish ApplicationReadyEvent (Application is fully initialized and ready for production traffic)
When Does the Embedded Web Server (Tomcat) Start?
A common senior interview question is: “At what exact lifecycle stage is the embedded Tomcat/Jetty server started?”
The web server starts inside the context.refresh() method, specifically during the onRefresh() hook of ServletWebServerApplicationContext:
- Spring retrieves the configured
ServletWebServerFactorybean (by default,TomcatServletWebServerFactory). - It programmatically instantiates the embedded
WebServerinstance, configures connectors, and binds to the network port (e.g. 8080). - This occurs after
BeanFactoryPostProcessorshave run, but before regular application singleton beans are eagerly instantiated. - The
DispatcherServletis then registered and mapped to the embedded server before the context finishes refreshing.
🔴 Senior Level
Deep Dive: context.refresh() Phases
The core of SpringApplication.run() is AbstractApplicationContext.refresh(). In the context of @SpringBootApplication, the critical phases execute in this order:
invokeBeanFactoryPostProcessors(beanFactory):- Spring invokes
ConfigurationClassPostProcessor. - It parses
@SpringBootApplicationand executes@ComponentScanusing the ASM bytecode reader to createBeanDefinitions for all application components. - It executes
AutoConfigurationImportSelector, reading the.importsregistry, evaluating@Conditionalconditions, and registering auto-configurations.
- Spring invokes
registerBeanPostProcessors(beanFactory):- Instantiates and registers all
BeanPostProcessors (such asAutowiredAnnotationBeanPostProcessorandAnnotationAwareAspectJAutoProxyCreator).
- Instantiates and registers all
onRefresh():- Overridden in
ServletWebServerApplicationContextto start the embedded web server (Tomcat, Jetty, or Undertow).
- Overridden in
finishBeanFactoryInitialization(beanFactory):- Calls
beanFactory.preInstantiateSingletons(): instantiates, injects dependencies, and initializes all non-lazy singleton beans.
- Calls
finishRefresh():- Publishes
ContextRefreshedEventand starts anySmartLifecyclecomponents.
- Publishes
The Disaster of Placing @SpringBootApplication in the Default Package
If a developer declares the main application class without a package declaration (package com.example... is omitted):
- The class resides in Java’s Default Package (root).
- Because
@ComponentScandefaults to scanning the declaring package and all subpackages, Spring Boot attempts to scan the entire JVM classpath. - Every single
.classfile inside every dependency JAR in the project is scanned for annotations. - Consequences: Startup time balloons from 2 seconds to several minutes, massive Metaspace and heap consumption occurs, and conflicting classes in third-party libraries trigger fatal dependency resolution crashes.
Diagnostics via FailureAnalyzer
When startup fails (e.g., port 8080 is already bound, or an unresolvable dependency cycle exists), Spring Boot catches the exception and routes it through registered implementations of the FailureAnalyzer SPI:
============================
APPLICATION FAILED TO START
============================
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
Instead of printing a raw 300-line stack trace, analyzers like PortInUseFailureAnalyzer and NoSuchBeanDefinitionFailureAnalyzer produce human-readable descriptions and concrete remediation actions.
Post-Startup Execution: CommandLineRunner vs ApplicationRunner
After the ApplicationContext is fully refreshed and the web server is listening, Spring Boot executes all beans implementing runner interfaces before entering the steady state:
| Runner Interface | Method Signature | Argument Handling |
|---|---|---|
CommandLineRunner |
run(String... args) |
Receives raw, unparsed string array: ["--port=9090", "debug"] |
ApplicationRunner |
run(ApplicationArguments args) |
Provides parsed access: args.getOptionValues("port"), args.getNonOptionArgs() |
@Component
public class DatabaseSeeder implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
if (args.containsOption("seed-demo-data")) {
System.out.println("Seeding demonstration records into database...");
}
}
}
4 Tricky Questions
1. What disastrous consequences occur if a developer places @SpringBootApplication in the Java default package (no package declaration)?
Answer:
If @SpringBootApplication is placed in the default package, @ComponentScan without an explicit scanBasePackages defaults to scanning the entire root package of the JVM.
This causes Spring’s component scanner to traverse the entire classpath, inspecting every class inside every dependency JAR (Spring Framework, Hibernate, Jackson, JDBC drivers, etc.). This leads to:
- Drastic startup degradation (startup time increases by orders of magnitude).
- Metaspace and memory exhaustion from loading thousands of class bytecodes into ASM metadata readers.
- Accidental component registration of internal classes within third-party JARs, triggering
BeanDefinitionStoreExceptionor dependency injection conflicts.
2. At what exact lifecycle phase is the embedded Tomcat/Jetty server instantiated and started during SpringApplication.run()?
Answer:
The embedded web server is instantiated and started inside the context.refresh() phase, specifically during the onRefresh() hook method of ServletWebServerApplicationContext.
At this point, all BeanFactoryPostProcessors (including component scanning and auto-configuration loading) have completed, and BeanPostProcessors are registered, but application singleton beans have not yet been instantiated. Spring calls ServletWebServerFactory.getWebServer() to start the server connector, binding the network port before eager singletons are initialized.
3. What is the fundamental difference between CommandLineRunner and ApplicationRunner, and when do they execute?
Answer:
Both runner interfaces execute strictly after context.refresh() completes and the embedded web server is started, but before SpringApplication.run() returns and the application enters steady state.
The difference lies in argument parsing:
CommandLineRunner: Passes raw CLI arguments as a plain Java string array (String... args).ApplicationRunner: Wraps arguments in anApplicationArgumentsobject, providing pre-parsed access to option arguments (--server.port=9090viagetOptionValues()) and non-option arguments.
4. Why does Spring Boot introduce @SpringBootConfiguration instead of simply using Spring Framework’s standard @Configuration?
Answer:
@SpringBootConfiguration is a meta-annotation that encapsulates @Configuration. Its primary technical role is to serve as an anchor marker for Spring Boot testing infrastructure.
When running integration slice tests (such as @SpringBootTest, @WebMvcTest, or @DataJpaTest), Spring’s test framework automatically searches upward through the package hierarchy from the test class until it finds a class annotated with @SpringBootConfiguration. This allows the test framework to locate the root application configuration without requiring developers to specify test configuration classes manually.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
@SpringBootApplicationis the primary composite bootstrap annotation in Spring Boot, combining:
@SpringBootConfiguration: Declares the class as a configuration source and serves as the configuration anchor for@SpringBootTest.@EnableAutoConfiguration: Triggers auto-configuration discovery viaAutoConfigurationImportSelector.@ComponentScan: Scans the current package and all child subpackages for@Component,@Service,@Repository, and@Controllerbeans.When
SpringApplication.run()executes, it prepares the environment, creates theApplicationContext, starts the embedded web server (Tomcat) inonRefresh(), eagerly instantiates all singletons, runsCommandLineRunner/ApplicationRunnerbeans, and publishesApplicationReadyEvent.
Composition Breakdown Matrix
| Constituent Annotation | Core Purpose | Default Scope |
|---|---|---|
@SpringBootConfiguration |
Defines @Bean definitions and test root |
Declaring class |
@EnableAutoConfiguration |
Loads classpath-based auto-configurations | Global context |
@ComponentScan |
Discovers application components | Declaring package + subpackages |
Red Flags (DO NOT Say)
- ❌ “@SpringBootApplication is just a syntax shortcut for public static void main.” (It is a composite configuration, scanning, and auto-configuration meta-annotation).
- ❌ “Tomcat starts before the Spring ApplicationContext is created.” (Tomcat is created and started inside
context.refresh()during theonRefresh()phase). - ❌ “You can put the main application class in the default root package without issues.” (This forces
@ComponentScanto scan all dependency JARs on the classpath, severely degrading startup). - ❌ “CommandLineRunner executes before beans are initialized.” (Runners execute strictly after the context is fully refreshed and all singletons exist).