The Spring Framework is the underlying platform: the IoC container, the AOP module, Spring MVC, Spring Data, Spring Security, and so on. It's powerful and flexible, but historically required a fair amount of manual configuration — declaring beans explicitly, wiring a DispatcherServlet, configuring a data source, choosing and configuring a servlet container — even for a simple application.
Spring Boot is built on top of Spring, adding an opinionated, convention-over-configuration layer:
- Auto-configuration: Spring Boot inspects what's on the classpath (and what beans you've already defined) and automatically configures sensible defaults — e.g., if
spring-boot-starter-weband an embedded Tomcat are present, it auto-configures aDispatcherServletand starts an embedded web server, with no explicit XML/Java config required. - Starter dependencies: curated, versioned dependency bundles (
spring-boot-starter-web,spring-boot-starter-data-jpa) that pull in a coherent, tested set of libraries for a given purpose, instead of manually assembling and version-matching individual JARs. - Embedded servers: Tomcat/Jetty/Undertow bundled directly into the runnable JAR — no separate application server installation/deployment needed.
- Production-ready features: Actuator (health checks, metrics, monitoring endpoints) out of the box.
@SpringBootApplication // Spring Boot's entry point — enables auto-configuration, component scanning, and config
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args); // starts an embedded server, wires everything
}
}
In one line: Spring Framework is the engine; Spring Boot is the opinionated, batteries-included way of assembling and running that engine with minimal ceremony — every auto-configured default can still be overridden explicitly when the default doesn't fit.
Related Resources
@SpringBootApplication is a single, convenient meta-annotation that bundles together three separate annotations, each contributing a distinct piece of behavior:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication { ... }
-
@SpringBootConfiguration— itself a specialization of the core Spring@Configurationannotation, marking this class as a source of bean definitions (so@Beanmethods inside it are honored) and identifying it, specifically, as the application's primary configuration class (useful for testing tools to locate it). -
@EnableAutoConfiguration— the annotation that actually triggers Spring Boot's auto-configuration machinery: based on what's present on the classpath and what beans already exist, Spring Boot conditionally activates a large set of pre-built configuration classes (for aDataSource, aDispatcherServlet, Jackson'sObjectMapper, and so on). -
@ComponentScan— scans the package containing the annotated class, and all its subpackages, for@Component/@Service/@Repository/@Controller-annotated classes to register as beans.
That third point is exactly why the conventional Spring Boot project structure places the @SpringBootApplication-annotated main class in the application's root package — anything outside that package tree (and its subpackages) won't be picked up by component scanning unless explicitly configured otherwise.
com.example.myapp
├── MyApp.java @SpringBootApplication — scans everything under com.example.myapp
├── controller/OrderController.java
├── service/OrderService.java
└── repository/OrderRepository.java
You can use the three annotations individually instead of @SpringBootApplication if you need finer-grained control (e.g., customizing @ComponentScan's base packages independently) — @SpringBootApplication is purely a convenience default for the common case.
Auto-configuration classes are just regular @Configuration classes — nothing magical about their structure — bundled inside Spring Boot's spring-boot-autoconfigure module (and inside third-party starter JARs). What makes them "auto" is two things working together:
1. Discovery: Spring Boot loads a list of candidate auto-configuration classes from a file at META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports on the classpath (this replaced the older spring.factories-based mechanism in Spring Boot 2.7+/3.x). Every listed class is a candidate to be evaluated — not necessarily applied.
2. Conditional activation: each auto-configuration class is guarded by one or more @Conditional-family annotations, so it only actually contributes bean definitions if its conditions hold:
@Configuration
@ConditionalOnClass(DataSource.class) // only if a DataSource class is on the classpath
@ConditionalOnMissingBean(DataSource.class) // only if the app hasn't already defined its own DataSource
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
Common condition annotations:
@ConditionalOnClass/@ConditionalOnMissingClass— is a given class present/absent on the classpath (used to detect "is this dependency actually included").@ConditionalOnBean/@ConditionalOnMissingBean— does a bean of a given type already exist in the context (this is exactly what lets your own@Beandefinition override an auto-configured default — if you define your ownDataSourcebean, the auto-configured one backs off).@ConditionalOnProperty— is a specific configuration property set (and optionally, to a specific value).@ConditionalOnWebApplication— is this a web application context at all.
This design is exactly why Spring Boot feels "magical" but remains fully overridable: adding spring-boot-starter-data-jpa to the classpath makes JPA-related conditions become true, activating a cascade of auto-configuration; defining your own equivalent bean explicitly makes the corresponding @ConditionalOnMissingBean condition false, causing Boot's default to quietly step aside in favor of yours.
@EnableAutoConfiguration(exclude = ...) (or the spring.autoconfigure.exclude property) lets you explicitly disable a specific auto-configuration class you don't want, even if its conditions would otherwise be satisfied.
Related Resources
A Spring Boot starter is a special dependency that contains no code of its own — it exists purely as a pom.xml/build.gradle entry whose only purpose is to transitively pull in a curated, mutually-compatible set of other libraries for a specific capability:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Adding just that one line brings in Spring MVC, an embedded Tomcat, Jackson (for JSON), and validation support — all at versions Spring Boot's team has already tested together, rather than requiring you to individually select and version-match each of those libraries yourself.
Why this matters:
- Eliminates "dependency hell" — historically, assembling a working set of compatible library versions by hand was a common source of runtime
NoSuchMethodError/ClassNotFoundExceptionissues from mismatched versions; starters (combined with Spring Boot's dependency management BOM) guarantee a tested, coherent set. - Directly drives auto-configuration: adding
spring-boot-starter-data-jpaputs Hibernate/Spring Data classes on the classpath, which is exactly the signal@ConditionalOnClass-guarded auto-configuration classes are watching for — the starter and the auto-configuration mechanism are designed to work together. - Communicates intent clearly —
spring-boot-starter-securityin apom.xmlimmediately tells a reader "this app uses Spring Security," which a long list of individually-chosen transitive dependencies wouldn't.
Common starters: spring-boot-starter-web (MVC + embedded server), spring-boot-starter-data-jpa (Spring Data JPA + Hibernate), spring-boot-starter-security, spring-boot-starter-test (JUnit, Mockito, AssertJ, Spring Test), spring-boot-starter-actuator (production monitoring endpoints).
Unlike traditional Java EE deployment (build a WAR, deploy it to a separately-installed application server), Spring Boot embeds the servlet container directly inside the application's own runnable JAR — the server runs as a library, inside the same JVM process, started by SpringApplication.run().
Default: spring-boot-starter-web pulls in spring-boot-starter-tomcat transitively, so Tomcat is the out-of-the-box default embedded container.
Switching containers just means adjusting dependencies — exclude the default and add the alternative starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Auto-configuration detects whichever server's classes are actually present on the classpath and configures that one — no other code changes needed, since Spring Boot's ServletWebServerFactory abstraction (TomcatServletWebServerFactory, JettyServletWebServerFactory, UndertowServletWebServerFactory) hides the container-specific setup behind a common interface.
Configuration happens through the same standard application.properties/application.yml keys regardless of which container is active:
server.port=8443
server.tomcat.max-threads=200
server.compression.enabled=true
Lifecycle: the embedded server starts as part of ApplicationContext refresh and stops as part of context shutdown — there's no separate "deploy" step; running the JAR (java -jar app.jar) both starts the Spring container and the web server together, which is a large part of why Spring Boot applications are simple to containerize (a Docker image just needs a JRE and the JAR).
Choosing between them in practice: Tomcat is the safe, well-understood default for most applications; Undertow is often chosen for a lighter memory footprint and strong non-blocking I/O performance (frequently paired with reactive/WebFlux applications); Jetty is a solid alternative with its own tuning characteristics, sometimes preferred in existing Jetty-based infrastructure.