What is Spring Boot, and What Problem Does It Solve?
Spring Boot is not a different framework from Spring — it's
an opinionated layer built on top of Spring Framework (covered in
Spring Core) that eliminates manual configuration.
Before Boot, building a Spring web application meant hand-writing XML or Java
configuration to wire a DispatcherServlet, configure a
DataSource, set up a TransactionManager, and deploy
a WAR to an external Tomcat — hours of setup before writing a single line of
business logic.
The problem Boot solves: for the overwhelming majority of applications, that
configuration is the same boilerplate every time. Boot inspects what
libraries are on your classpath and automatically configures sensible
defaults — add spring-boot-starter-web, and Boot infers "this is
a web app", wires an embedded Tomcat, registers a
DispatcherServlet, and configures Jackson for JSON — all before
you write any configuration code. You override only what genuinely differs
from the defaults.
| Aspect | Traditional Spring | Spring Boot |
|---|---|---|
| Configuration | Manual XML or Java @Configuration classes |
Auto-configuration, override only what differs |
| Server | Deploy WAR to external Tomcat | Embedded server, runs as java -jar |
| Dependencies | Manage each version individually | Starters + BOM manage compatible versions together |
| Setup time | Hours of wiring before "Hello World" runs | Minutes — generate at start.spring.io, run immediately |
The Entry Point: @SpringBootApplication
package com.company.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyAppApplication {
public static void main(String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
}
// @SpringBootApplication is a meta-annotation — shorthand for three separate ones:
@Configuration // this class itself can declare @Bean methods
@EnableAutoConfiguration // triggers the auto-configuration mechanism (see below)
@ComponentScan // scans this package and sub-packages for @Component/@Service/etc.
# Running it
mvn spring-boot:run # dev mode, via the Maven plugin
mvn clean package
java -jar target/my-app-1.0.0.jar # the actual deployable artifact
java -jar my-app.jar --spring.profiles.active=prod # activate a profile
java -Dserver.port=9090 -jar my-app.jar # override a property via system property
How Auto-Configuration Actually Works
This is the part most tutorials wave away as "magic". It isn't — it's a deliberate, inspectable mechanism, and understanding it is what separates someone who uses Spring Boot from someone who can debug it when it does something unexpected.
/*
* At startup, @EnableAutoConfiguration triggers this sequence:
*
* 1. Spring Boot reads a file bundled inside every starter JAR:
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
* (renamed from the old spring.factories in Boot 2.7+)
*
* 2. This file lists candidate @Configuration classes — e.g. spring-boot-
* autoconfigure.jar lists DataSourceAutoConfiguration, WebMvcAutoConfiguration,
* JacksonAutoConfiguration, and ~200 others.
*
* 3. EACH candidate class is conditionally evaluated using @ConditionalOnClass,
* @ConditionalOnMissingBean, @ConditionalOnProperty, etc.
*
* 4. Only configurations whose conditions are satisfied actually register beans.
* The rest are silently skipped — no error, no log by default, just absent.
*/
A real auto-configuration class, simplified
// This is genuinely close to what DataSourceAutoConfiguration looks like internally
@Configuration
@ConditionalOnClass(DataSource.class) // only activates if a JDBC DataSource class exists on the classpath
@ConditionalOnMissingBean(DataSource.class) // only if YOU haven't already defined your own DataSource bean
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
// This is WHY defining your own @Bean DataSource silently disables Boot's
// default — @ConditionalOnMissingBean backs off the moment yours exists.
// No error, no warning — your bean simply wins.
# Exclude specific auto-configurations explicitly
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
SecurityAutoConfiguration.class
})
public class MyApp { }
# See EXACTLY what was applied and what was skipped, and why —
# the single most useful debugging command for "why isn't my bean here"
java -jar my-app.jar --debug
# Prints a full report: "Positive matches" (applied) and "Negative matches"
# (skipped, with the exact condition that failed) for every candidate
If you define your own @Bean of a type Boot also tries to
auto-configure, @ConditionalOnMissingBean almost always means
yours wins silently — Boot's auto-config simply doesn't fire. This is by
design, but it's also the source of most "why is my custom configuration
being ignored / why is it working differently than I configured" bug
reports. The --debug report is the fastest way to confirm
whether a given auto-configuration actually ran.
A Minimal REST API
// Model — a record, not a class with manual getters/setters/constructors.
// Immutable, equals/hashCode/toString generated, accessors without "get" prefix.
public record User(Long id, String name, String email) {}
// Service — stateless singleton bean, constructor injection
@Service
public class UserService {
private final Map<Long, User> users = new ConcurrentHashMap<>();
private final AtomicLong nextId = new AtomicLong(1);
public List<User> findAll() { return new ArrayList<>(users.values()); }
public Optional<User> findById(Long id) { return Optional.ofNullable(users.get(id)); }
public User create(String name, String email) {
User user = new User(nextId.getAndIncrement(), name, email);
users.put(user.id(), user);
return user;
}
}
// Controller — thin, delegates everything to the service
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) { this.userService = userService; }
@GetMapping
public List<User> getAll() { return userService.findAll(); }
@GetMapping("/{id}")
public ResponseEntity<User> getOne(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User create(@RequestBody CreateUserRequest request) {
return userService.create(request.name(), request.email());
}
}
// Separate request DTO from the domain record — never bind @RequestBody
// directly to your entity/domain type in real applications (see Spring REST page)
public record CreateUserRequest(String name, String email) {}
Full HTTP verb coverage, validation, error handling, and DTO patterns are covered in depth in Spring REST — this is the minimal shape to get something running.
DevTools — Faster Local Development
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional> <!-- doesn't propagate transitively to projects that depend on YOUR jar -->
</dependency>
DevTools triggers an automatic restart when classpath files change, disables
template/cache defaults that would otherwise hide your edits, and
automatically excludes itself from a production build via
spring-boot-maven-plugin's repackage step — you don't need to
remember to remove it before shipping.
Configuration, Profiles, and Starters — Where to Go Next
Three large topics that deserve their own pages rather than a cramped summary here:
- Application Properties —
application.properties/.yml,@Valuevs@ConfigurationProperties, externalised config precedence order. - Profiles & Environments —
spring.profiles.active, per-environment property files,@Profile-conditional beans. - Spring Boot Starters — what a starter actually is, how to read one, building your own custom starter.
Actuator — Production Observability
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.properties — endpoints are NOT exposed by default except /health
management.endpoints.web.exposure.include=health,info,metrics,env
management.endpoint.health.show-details=when-authorized
| Endpoint | Purpose |
|---|---|
/actuator/health | Liveness/readiness — what Kubernetes probes hit |
/actuator/metrics | Micrometer metrics — JVM, HTTP, custom |
/actuator/env | Resolved environment properties — sensitive, secure it |
/actuator/beans | Full bean graph — invaluable for debugging DI issues |
/actuator/mappings | Every registered @RequestMapping path |
/actuator/env, /actuator/heapdump, and
/actuator/beans can leak secrets, memory contents, or
internal architecture to anyone who can reach them. In production, expose
only what's needed (health, info,
metrics for monitoring scrapers), put Actuator behind
authentication, and ideally bind it to a separate management port
(management.server.port) not reachable from the public
internet.
Common Pitfalls
// ❌ MyAppApplication is in com.company.myapp — @ComponentScan only
// scans THIS package and below. A @Service in com.othercompany.lib is invisible.
package com.company.myapp;
@SpringBootApplication
public class MyAppApplication { }
// ✅ Explicitly widen the scan, or restructure packages so everything
// genuinely sits under the application's root package
@SpringBootApplication(scanBasePackages = {"com.company.myapp", "com.othercompany.lib"})
public class MyAppApplication { }
Packaging as WAR for an external servlet container is rarely necessary in 2026 — it exists mainly for organisations with established Tomcat/ Jakarta EE infrastructure they're not ready to retire. For new projects, the executable JAR with embedded server is simpler, easier to containerise (the entire deployment unit is one JAR), and is what Spring Boot's own defaults assume.
Interview Questions
Q: What is the difference between Spring and Spring Boot?
Spring Framework is the underlying technology (IoC container, DI, MVC, AOP).
Spring Boot is built on top of it and adds auto-configuration, an embedded
server, and curated dependency starters — eliminating most of the manual
setup Spring traditionally required. Boot doesn't replace Spring; everything
you write in a Boot app is still ordinary Spring code.
Q: What three annotations does @SpringBootApplication combine?
@Configuration (this class can declare beans),
@EnableAutoConfiguration (triggers Boot's classpath-based
auto-configuration), and @ComponentScan (scans this package and
below for stereotype-annotated classes).
Q: Why does a Spring Boot app run as an executable JAR instead of being deployed to Tomcat?
Boot embeds the server (Tomcat by default) directly inside the application's
own JAR. The application is self-contained and starts with
java -jar app.jar — no separate server installation, no WAR
deployment step, simpler containerisation.
Q: Explain the mechanism behind auto-configuration — not just what it does, but how.
At startup, @EnableAutoConfiguration reads
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
(the modern replacement for spring.factories, since Boot 2.7)
from every starter on the classpath. This lists candidate
@Configuration classes. Each candidate is evaluated against
conditions — typically @ConditionalOnClass (is the relevant
library actually present?) and @ConditionalOnMissingBean (has
the developer already defined their own bean of this type?). Only
configurations whose conditions pass register beans; the rest are silently
skipped. This is why defining your own DataSource bean
transparently disables Boot's auto-configured one, with no explicit
exclusion needed.
Q: How would you debug a missing or unexpected bean caused by auto-configuration?
Run with --debug (or debug=true in properties) —
Boot prints a full "Auto-configuration report" listing every candidate
configuration as a Positive match (applied) or Negative match (skipped, with
the exact failing condition named). This is the fastest way to confirm
whether a specific auto-configuration ran and why it didn't, rather than
guessing from documentation. /actuator/beans (Actuator) is the
complementary tool — it shows the actual resolved bean graph at runtime.
Q: Why is exposing /actuator/env or /actuator/heapdump publicly a real security risk?
/actuator/env returns all resolved property sources, often
including database credentials, API keys, and secrets pulled from environment
variables or config servers — verbatim. /actuator/heapdump
returns a full JVM heap dump, which can contain in-memory secrets, session
tokens, or PII from any object currently alive. The default
management.endpoints.web.exposure.include in modern Boot
versions is intentionally minimal (just health) precisely
because of these risks — widening it to * in production is a
common and serious misconfiguration found in security audits.