If you already write Java, you have probably heard of Spring as "the framework everyone uses in the enterprise world". And you have probably also heard the historic complaint that came with it: that configuring it was a long, repetitive chore full of XML files. Spring Boot was born precisely to remove that pain. This lesson explains the specific problem Spring Boot solves, how it solves it through four very recognisable mechanisms, and where it fits inside the Spring ecosystem. By the end you will also have met CicloUrbana, the real application you will build step by step throughout the course and that will give practical meaning to every concept.
Contents
- Spring Framework: the starting point
- "Configuration hell": a real side-by-side example
- The four pillars of Spring Boot
- Spring Boot within the Spring ecosystem
- What kind of applications are built with Spring Boot
- The course project: CicloUrbana
- Spring Boot 3.x versions and Java requirements
- Common Mistakes and Tips
- Exercises
- Spring Framework: the starting point
Before you can understand Spring Boot you need to understand what Spring Framework is, because Spring Boot does not replace it: it wraps it.
Spring Framework appeared in 2003 with one central idea: the inversion of control container (IoC, Inversion of Control). Instead of every class creating the objects it needs with new, the framework creates those objects —called beans— and hands them to whoever asks for them. That is what we call dependency injection.
The payoff is immediate:
- Classes depend on interfaces, not on concrete implementations.
- The code is easy to test, because in a test you can inject a fake implementation.
- The configuration of "what is wired to what" lives in a single place instead of being scattered across the code.
On top of that core, Spring kept adding modules: data access, transactions, MVC for the web, security, messaging... All of it excellent. The problem was never the power, it was the cost of getting started.
- "Configuration hell": a real side-by-side example
Until roughly 2014, getting a Spring web application up and running with one HTTP endpoint and a database connection required all of this groundwork:
- Create a Maven project and hand-pick the version of every library (Spring Core, Spring MVC, Spring JDBC, Jackson, a connection pool, the JDBC driver...), praying they were compatible with each other.
- Write a
web.xmlto register Spring's servlet. - Write one or more XML context files declaring the beans.
- Package a WAR file and deploy it to a separately installed application server (Tomcat, JBoss, WebLogic).
Here is how something as basic as a data source and a transaction manager was declared in the classic style:
<!-- applicationContext.xml — classic Spring configuration (old style) -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="...">
<!-- 1. The connection pool has to be declared by hand -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/ciclourbana"/>
<property name="username" value="ciclo"/>
<property name="password" value="secret"/>
<property name="maxTotal" value="20"/>
</bean>
<!-- 2. The transaction manager has to be declared and wired to the dataSource -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3. @Transactional support has to be switched on explicitly -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 4. And Spring has to be told where to look for your classes -->
<context:component-scan base-package="com.ciclourbana"/>
</beans>Plus a web.xml to boot all of that inside the server:
<!-- web.xml — required so the application server loads Spring -->
<web-app>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>That is roughly 40 lines of configuration that deliver no business functionality whatsoever: there is not a single bike station or rental in sight. It is pure scaffolding. And it has to be repeated, with variations, in every new project.
The same thing with Spring Boot
With Spring Boot, that entire block shrinks to one properties file and one startup class:
# src/main/resources/application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/ciclourbana
spring.datasource.username=ciclo
spring.datasource.password=secretpackage com.ciclourbana;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CicloUrbanaApplication {
public static void main(String[] args) {
SpringApplication.run(CicloUrbanaApplication.class, args);
}
}An explanation of what just happened:
- Spring Boot detects that there is a PostgreSQL driver on the classpath and that a
spring.datasource.urlproperty exists. With that it creates theDataSourcefor you, using HikariCP as the connection pool. - It also detects that there is a
DataSource, creates the transaction manager and switches on@Transactionalsupport. - The
component-scanswitches itself on, rooted at the package that holds the class annotated with@SpringBootApplication(com.ciclourbana). - No
web.xmlis needed because there is no external server: the server lives inside the application.
The comparison in short:
| Aspect | Classic Spring (pre-Boot) | Spring Boot |
|---|---|---|
| Library versions | Hand-picked and reconciled | Managed by the parent / BOM |
| Infrastructure configuration | Lengthy XML or @Configuration |
Autoconfiguration + properties |
| Web server | External, installed separately | Embedded in the JAR |
| Deployment artefact | WAR deployed into a container | Executable JAR (java -jar) |
| Time to the first endpoint | Hours | Minutes |
| Metrics, health, logs | Added by hand | Actuator, production-ready |
Key idea: Spring Boot is not a new framework competing with Spring. It is a layer of opinion and automation on top of Spring Framework. Everything you know about Spring still holds; you simply stop writing the boring part.
- The four pillars of Spring Boot
Almost everything Spring Boot does comes down to four mechanisms. It pays to be clear about them from day one, because you will meet them again and again.
3.1 Autoconfiguration
Spring Boot looks at what is on the classpath and at what you have configured, and from that it automatically registers the beans you would normally need. If it finds the Spring MVC library, it configures a DispatcherServlet. If it finds a JDBC driver and a URL, it configures a DataSource.
The golden rule is that autoconfiguration is conditional and steps aside: if you define your own bean, Spring Boot backs off and uses yours. It never blocks you. The internal mechanism (the @Conditional... annotations) is studied in detail in module 2, lesson 02-06.
3.2 Starters
A starter is a Maven dependency that contains no code, but rather a curated list of dependencies that work well together. Instead of adding eight libraries with their versions, you add one:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>That single line pulls in Spring MVC, Jackson (for JSON), validation, logging and an embedded Tomcat, all at mutually compatible versions. Notice there is no <version> tag: the version is decided by the Spring Boot parent. Starters are covered in lesson 01-04 and their inner workings in 02-06.
3.3 Embedded web server
A Spring Boot web application contains its own server. Running java -jar ciclourbana.jar starts a Tomcat (or Jetty, or Undertow) inside the very same Java process.
The consequences are far-reaching:
- The artefact is self-contained: a single JAR file with everything inside.
- The application runs the same way on your laptop as it does in production.
- It fits naturally with Docker containers and cloud platforms, where the model is "one process, one port".
- The whole class of "it works on my machine, but the server's Tomcat is a different version" errors simply disappears.
3.4 Production-ready features
With a single dependency (Actuator), Spring Boot ships everything an application needs to live in production:
- Health endpoints (
/actuator/health) that a load balancer or Kubernetes can query. - Metrics (memory, requests per second, latencies) exportable to Prometheus.
- Application information, a dump of the effective configuration, and log level changes at runtime.
All of this is covered in modules 7 and 9. I mention it here only because it exists, because it is one of the four pillars and it explains why Spring Boot is the de facto enterprise standard.
- Spring Boot within the Spring ecosystem
"Spring" is a family of projects. This table places each piece and tells you in which module of the course it will show up:
| Project | What it brings | When will you use it in CicloUrbana? |
|---|---|---|
| Spring Framework | The core: IoC container, dependency injection, AOP, transactions, Spring MVC | Modules 2 and 3 |
| Spring Boot | Autoconfiguration, starters, embedded server, Actuator | The whole course |
| Spring Data JPA | Automatic repositories over relational databases | Module 4 |
| Spring Security | Authentication, authorisation, API protection, JWT | Module 5 |
| Spring Test | Testing utilities integrated with JUnit and Mockito | Module 6 |
| Spring Cloud | Distributed configuration, service discovery, fault tolerance | Module 7 (introduction) |
| Spring Batch / Integration | Batch processing and systems integration | Outside the scope of the course |
Think of Spring Boot as the integrator: it does not reimplement Spring Data or Spring Security, it autoconfigures them so that they work the moment you add their starter.
- What kind of applications are built with Spring Boot
Spring Boot is not limited to REST APIs, even if that is its most common use:
- REST APIs and backends for web or mobile applications. This is the majority case, and the one this course follows.
- Microservices. Each service is a self-contained JAR with its own port and its own database.
- Traditional web applications with server-side views (Thymeleaf, FreeMarker).
- Command-line applications and batch processes, with no web server, using
CommandLineRunner(lesson 01-05). - Messaging consumers (Kafka, RabbitMQ) or scheduled tasks (module 7).
- GraphQL APIs or reactive services with Spring WebFlux, for extremely high concurrency loads.
- The course project: CicloUrbana
Throughout the course you will build CicloUrbana, the platform that manages the municipal electric bike network of Ribalta, a fictional medium-sized city.
The business context
Ribalta's city council has installed docking stations across the city —"Main Square", "North Station", "River Park" and "University", among others—. Each station has a limited number of docks and a set of geographic coordinates. They hold electric bikes identified by plates such as RB-0142, each with a battery level and a status.
A registered user unlocks a bike at one station, rides it and returns it at another station. That journey is a rental, charged according to a fare. If something goes wrong —a broken brake, a battery that will not charge—, an incident is opened.
The actors
| Actor | What they do in the system |
|---|---|
| Citizen user | Browses stations and available bikes, starts and finishes rentals, views their history |
| Maintenance operator | Receives incidents, changes bike statuses, rebalances stations |
| Council manager | Reviews usage statistics, manages fares, registers new stations |
| External system | The mobile app and the station information panels consume the API |
The API you will end up with
By the end of the course, CicloUrbana will expose a REST API versioned under /api/v1. A preview of its final shape:
| Method and path | Purpose |
|---|---|
GET /api/v1/stations |
List stations, with filters and pagination |
GET /api/v1/stations/{id} |
Details of one station |
POST /api/v1/stations |
Register a station (manager only) |
GET /api/v1/stations/{id}/bikes |
Bikes available at a station |
POST /api/v1/rentals |
Start a rental |
POST /api/v1/rentals/{id}/finish |
Return the bike and calculate the amount |
POST /api/v1/incidents |
Report a fault |
POST /api/v1/auth/login |
Obtain a JWT token |
The target architecture
flowchart TD
subgraph Clients
A["Mobile app"]
B["Station panel"]
C["Council back office"]
end
A --> API
B --> API
C --> API
subgraph "CicloUrbana (Spring Boot)"
API["Web layer<br/>REST controllers /api/v1"]
SEC["Spring Security<br/>JWT + roles"]
SRV["Service layer<br/>Business rules"]
REP["Spring Data JPA<br/>Repositories"]
ACT["Actuator<br/>health and metrics"]
API --> SEC
SEC --> SRV
SRV --> REP
end
REP --> DB[("PostgreSQL")]
ACT --> MON["Prometheus / Grafana"]
Do not worry if some of the boxes are unfamiliar: each one corresponds to a module of the course. In module 1 you will only build the web layer with fixed in-memory data; the rest will arrive in due course.
Project conventions
These decisions will stay unchanged for the whole course:
| Item | Value |
|---|---|
groupId |
com.ciclourbana |
artifactId |
ciclourbana |
| Base package | com.ciclourbana |
| Java | 21 (LTS) |
| Spring Boot | 3.x |
| Build | Maven, with the mvnw wrapper |
| API prefix | /api/v1 |
| Code language | Identifiers and comments in English |
- Spring Boot 3.x versions and Java requirements
Spring Boot 3 was a significant break with the 2.x branch:
- It requires Java 17 as a minimum. This course uses Java 21, the recommended LTS release, which brings records, pattern matching and virtual threads.
- Migration from
javax.*tojakarta.*. The persistence and validation annotations moved fromjavax.persistencetojakarta.persistence. If you copy old code from the internet and seejavax, it is out of date. - It builds on Spring Framework 6, with native support for observability (Micrometer) and for native compilation with GraalVM.
| Branch | Minimum Java | Namespace | Status |
|---|---|---|---|
| Spring Boot 2.7 | Java 8 | javax.* |
End of free support |
| Spring Boot 3.0–3.2 | Java 17 | jakarta.* |
Older branches of the 3 line |
| Spring Boot 3.3/3.4/3.5 | Java 17 (21 recommended) | jakarta.* |
Branches used in this course |
One practical detail: Spring Boot releases minor versions frequently. When you generate the project in lesson 01-03, Spring Initializr will offer you the latest stable release of the 3.x branch. Accept whatever it proposes; everything you learn here is valid on any 3.x.
Common Mistakes and Tips
- Believing that Spring Boot replaces Spring Framework. It does not. When you debug a problem, remember that underneath there is a perfectly ordinary Spring Framework
ApplicationContext. Learning the core (module 2) is what will get you unstuck. - Thinking autoconfiguration is uncontrollable magic. It is ordinary Java code, conditional and entirely inspectable. Any bean you define yourself takes precedence over the autoconfigured one.
- Following Spring Boot 2 tutorials without noticing. Warning signs:
javax.persistenceimports,WebSecurityConfigurerAdapter,antMatchers(). All of that has disappeared or changed in the 3 branch. - Adding dependencies with an explicit version. If the starter manages it, do not pin it yourself: you will break the compatibility the parent guarantees.
- Tip: get into the habit today of consulting the official documentation at
docs.spring.io. It is organised by version and is among the best in the Java ecosystem.
Exercises
Exercise 1
List the four infrastructure tasks a developer had to do by hand in classic Spring and explain, for each one, which pillar of Spring Boot removes it.
Exercise 2
A colleague tells you: "I've added spring-boot-starter-web but I want to use Jetty instead of Tomcat. With Spring Boot you can't choose that any more." Explain why that statement is wrong, drawing on the idea that autoconfiguration "steps aside".
Exercise 3
Design, without writing any code, the REST routes that CicloUrbana's maintenance operator would need in order to do their job. Give the HTTP method and the path under /api/v1, and justify each one briefly.
Solutions
Solution 1
| Manual task in classic Spring | Pillar that removes it | How |
|---|---|---|
| Choosing and reconciling library versions | Starters | A single dependency pulls in a compatible set; the version is pinned by the parent |
Declaring infrastructure beans (DataSource, TransactionManager, DispatcherServlet) in XML |
Autoconfiguration | Spring Boot creates them upon detecting libraries and properties |
| Installing and configuring an external application server and packaging a WAR | Embedded server | The server travels inside the executable JAR |
| Adding metrics, health checks and log management by hand | Production-ready features | Actuator exposes them with a single dependency |
Solution 2
The statement is wrong. spring-boot-starter-web includes Tomcat by default, but that is a reversible decision: it is enough to exclude the Tomcat starter and add the Jetty one.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- We remove the default server -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- And we add the one we want -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>Once Tomcat is gone from the classpath, the autoconfiguration that activated it no longer applies and Jetty's takes over. This is exactly the principle of "opinionated by default, reversible by decision": Spring Boot chooses for you, but it never ties your hands.
Solution 3
A reasonable proposal for the maintenance operator:
| Method and path | Rationale |
|---|---|
GET /api/v1/incidents?status=OPEN |
See their pending work queue; filtering by status avoids pulling in unnecessary history |
GET /api/v1/incidents/{id} |
Check the details of a specific fault before travelling out |
PATCH /api/v1/bikes/{plate} |
Change a bike's status (to MAINTENANCE, for example); PATCH because it is a partial modification |
POST /api/v1/incidents/{id}/resolve |
Mark the incident as resolved; modelled as an action because it triggers business logic, it is not a simple field change |
GET /api/v1/stations?occupancy=HIGH |
Spot saturated or empty stations in order to rebalance bikes |
Note: POST /api/v1/incidents/{id}/resolve deliberately breaks the strictest REST orthodoxy. It is a very common compromise and we will discuss it in detail in module 3.
Conclusion
Spring Boot is a layer of automation on top of Spring Framework that removes repetitive configuration through four pillars: autoconfiguration, starters, embedded server and production-ready features. It does not replace Spring: it makes it productive from minute one, without taking control away from you, because any automatic decision can be overridden.
You have also met CicloUrbana, Ribalta's electric bike network, which you will build layer by layer: first the web layer with in-memory data, then persistence, security, testing and finally deployment and monitoring.
In the next lesson, Setting Up Your Development Environment, you will get your machine ready: you will install JDK 21, understand why the course uses Maven, choose an IDE and API testing tools, and validate everything against a checklist before writing CicloUrbana's first line.
Spring Boot Course
Module 1: Introduction to Spring Boot
- What Is Spring Boot?
- Setting Up Your Development Environment
- Building Your First Spring Boot Application
- Understanding the Project Structure
- Application Startup and Lifecycle
Module 2: Spring Boot Core Concepts
- Spring Boot Annotations
- Dependency Injection in Spring Boot
- Bean Scope and Lifecycle
- Spring Boot Configuration
- Spring Boot Properties
- Auto-Configuration and Starters from the Inside
Module 3: Building RESTful Web Services
- Introduction to RESTful Web Services
- Creating REST Controllers
- Handling HTTP Methods
- Validating Input Data
- DTOs and Mapping Between Layers
- Exception Handling in REST
- Documenting the API with OpenAPI
Module 4: Data Access with Spring Boot
- Introduction to Spring Data JPA
- Configuring Data Sources
- Creating JPA Entities
- Relationships Between Entities
- Using Spring Data Repositories
- Query Methods in Spring Data JPA
- Transactions and Persistence Management
- Schema Migrations with Flyway
Module 5: Security in Spring Boot
- Introduction to Spring Security
- Configuring Spring Security
- User Authentication and Authorization
- Implementing JWT Authentication
- Method-Level Security and API Hardening
Module 6: Testing in Spring Boot
- Introduction to Testing
- Unit Testing with JUnit
- Mocking with Mockito
- Integration Testing
- Testing with Testcontainers
Module 7: Advanced Spring Boot Features
- Spring Boot Actuator
- Spring Boot Profiles
- Scheduled Tasks and Asynchronous Execution
- Spring Boot with Docker
- Spring Boot and Microservices
- Service Communication and Fault Tolerance
Module 8: Deploying Spring Boot Applications
- Introduction to Deployment
- Deploying to Heroku
- Deploying to AWS
- Deploying to Kubernetes
- Continuous Integration and Delivery
Module 9: Performance and Monitoring
- Performance Tuning
- Caching with Spring Cache
- Monitoring with Spring Boot Actuator
- Using Prometheus and Grafana
- Logging and Log Management
- Distributed Tracing
