1. Modern Java Essentials
Spring Boot 3 code — and this course's example code — leans on three Java features added since Java 8 that are easy to miss if your Java knowledge predates them: records, sealed classes, and pattern matching. None of them are Spring-specific, but you'll see all three constantly once DTOs and typed responses show up from Week 3 onward.
Records
A record is an immutable data carrier — it generates a constructor,
getters, equals(), hashCode() and toString()
from a one-line field list, replacing pages of boilerplate.
public record CustomerDto(Long id, String name, String email) {}
// Usage -- everything below is generated for you
CustomerDto dto = new CustomerDto(1L, "Ada Lovelace", "ada@example.com");
dto.name(); // "Ada Lovelace" -- accessor, not getName()
dto.equals(other); // structural equality, field by field
System.out.println(dto); // CustomerDto[id=1, name=Ada Lovelace, email=ada@example.com]
Sealed classes
A sealed class or interface restricts which other classes may extend or
implement it, listed explicitly with permits. Combined with pattern
matching, this lets the compiler verify you've handled every possible case.
public sealed interface PaymentResult
permits PaymentResult.Success, PaymentResult.Declined {
record Success(String transactionId) implements PaymentResult {}
record Declined(String reason) implements PaymentResult {}
}
Pattern matching for switch
Modern switch can match on a value's type (and even destructure a
record's fields directly), replacing a chain of instanceof checks and
casts with one readable block:
String describe(PaymentResult result) {
return switch (result) {
case PaymentResult.Success(String txId) ->
"Payment succeeded: " + txId;
case PaymentResult.Declined(String reason) ->
"Payment declined: " + reason;
};
}
// No default branch needed -- the compiler knows Success and Declined
// are the only two permitted implementations, and enforces exhaustiveness.
You'll write records constantly as request/response DTOs starting Week 3, and this sealed-interface-plus-switch pattern is exactly how you'll model "this API call either succeeded or failed in one of a few specific ways" throughout the course.
2. JDK 21 & Build Tools
This course uses Java 21, the current LTS (Long-Term Support) release — the version most production Spring Boot 3 services actually run on. Install it, then confirm it's active:
java -version
# openjdk version "21.0.x" ...
javac -version
# javac 21.0.x
A build tool compiles your code, resolves and downloads dependencies (like Spring Boot's own libraries), and packages the result. Spring supports two: Maven (XML-configured, more explicit) and Gradle (Groovy/Kotlin DSL, more concise). This course uses Maven for its examples because its verbosity makes what's happening easier to see the first time — but everything transfers directly to Gradle.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.codeverse</groupId>
<artifactId>week-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
</parent>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
The spring-boot-starter-parent is what makes Spring Boot's dependency
management work — it pins compatible versions of every Spring library so you don't
have to track them yourself. A starter like
spring-boot-starter-web is just a curated bundle of dependencies for
one job (here, building web APIs); you'll add more starters as the course goes on.
Don't hand-write a pom.xml from scratch. Use start.spring.io (Spring Initializr) to generate a working project with exactly the dependencies you pick — it's what every example in this course is built from.
3. Project Structure & Dependency Management
Every Spring Boot project generated by Initializr follows the same Maven-standard layout. Knowing it cold means you'll never have to guess where a file belongs:
week-01/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/codeverse/week01/
│ │ │ └── Week01Application.java ← entry point
│ │ └── resources/
│ │ └── application.properties ← config
│ └── test/
│ └── java/
│ └── com/codeverse/week01/
│ └── Week01ApplicationTests.java
src/main/java is your application code; src/main/resources
holds configuration and static files; src/test/java mirrors the main
package structure for tests. This convention-over-configuration layout is one reason
Spring Boot projects look similar across companies — you'll recognize it in Week 8's
testing module and every week after.
4. Your First Spring Boot Application
A Spring Boot app starts from one class annotated @SpringBootApplication
— a single annotation that bundles three things: component scanning, auto-configuration,
and marking this as the app's configuration root. You'll unpack exactly what each of
those does in Week 2; for now, treat it as "the switch that turns this class into a
runnable Spring app."
package com.codeverse.week01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class Week01Application {
public static void main(String[] args) {
SpringApplication.run(Week01Application.class, args);
}
}
@RestController
class HelloController {
@GetMapping("/hello")
String hello() {
return "Hello from Spring Boot on Java 21!";
}
}
SpringApplication.run() boots an embedded web server (Tomcat, by
default), scans your package for components like HelloController, and
wires everything together — all before your terminal prints its first log line.
You'll build real @RestController classes properly in Week 3; this one
exists just to prove the whole chain works end to end.
5. Running, Packaging & IDE Workflow
You can run a Spring Boot app straight from Maven, without packaging anything first — the fastest loop while you're actively coding:
./mvnw spring-boot:run
# Then, in another terminal:
curl http://localhost:8080/hello
# Hello from Spring Boot on Java 21!
For anything beyond local development, you package the app into a single executable JAR — no separate application server required, since Spring Boot embeds one:
./mvnw clean package
java -jar target/week-01-0.0.1-SNAPSHOT.jar
That single JAR is what you'll containerize with Docker in Week 13 — the exact same artifact runs locally and in production, which is a large part of why Spring Boot displaced older, application-server-based Java deployment models.
IntelliJ IDEA (the free Community Edition is enough for this course) has first-class Spring Boot support — run configurations, endpoint discovery, and a built-in HTTP client for testing routes like /hello without leaving the editor.
6. Hands-on Exercise
Generate, run and extend your first Spring Boot service
Get the full local loop working, then apply this week's modern-Java features to a small typed response.
Requirements:
- Generate a project at start.spring.io with Java 21, Maven, and the "Spring Web" dependency. Import it into your IDE.
- Confirm it runs with
./mvnw spring-boot:runand thatGET /or a route you add responds overcurlor your browser. - Define a
record Greeting(String message, int wordCount). - Add a
GET /greet?name=...endpoint returning aGreetingrecord (Spring will serialize it to JSON automatically) — have it default to"world"if nonameis supplied. - Model a
sealed interface GreetResultwithOk(Greeting greeting)andInvalid(String reason)variants; returnInvalidwhennameis blank or over 50 characters, and use a pattern-matchingswitchto turn the result into the right HTTP response.
You don't need a proper JSON error-response shape yet — that's Week 3's @ControllerAdvice topic. Returning a plain string for the Invalid case with the right status code is enough for this exercise.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What does declaring public record CustomerDto(Long id, String name) give you for free?
What does declaring public record CustomerDto(Long id, String name) give you for free?
A constructor taking both fields, field accessors (id() and name(), not getId()), and structurally-correct equals(), hashCode() and toString() implementations — all generated by the compiler from that one line, with the fields implicitly final and immutable.
Q2
Why can a pattern-matching switch over a sealed interface skip a default branch?
Why can a pattern-matching switch over a sealed interface skip a default branch?
A sealed type's permits clause tells the compiler the complete, closed set of implementations that can ever exist. If the switch covers every permitted case, the compiler knows no other case is possible and doesn't require a fallback — and if you add a new permitted type later without updating the switch, compilation fails instead of silently falling through.
Q3
What does spring-boot-starter-parent actually do in pom.xml?
What does spring-boot-starter-parent actually do in pom.xml?
It's a parent POM that pins a compatible set of dependency versions across the entire Spring ecosystem, so when you add a starter like spring-boot-starter-web you don't specify a version yourself and risk pulling in mismatched library versions. It also supplies default plugin configuration, like the plugin that builds the executable JAR.
Q4
Why doesn't a Spring Boot app need a separately installed application server like Tomcat?
Why doesn't a Spring Boot app need a separately installed application server like Tomcat?
Spring Boot embeds a servlet container (Tomcat by default) directly inside the executable JAR that ./mvnw clean package produces. Running java -jar app.jar starts the app and its web server together as one process, instead of deploying a WAR file into a separately managed server — this is what makes "the same artifact runs identically locally and in production" true.