Spring Boot 3 isn't an incremental release — it's the first version that requires Java 17, migrates to the Jakarta EE namespace, and ships a set of platform-level features that meaningfully change how you build and operate Java services. If you're still on Spring Boot 2.x, here's what's waiting on the other side and why the upgrade is worth it.
Spring Boot 3 at a glance
The baseline: Java 17+ and Jakarta EE
Spring Boot 3 dropped support for Java 8/11 and moved from the javax.* namespace to jakarta.*. This is the most disruptive part of the migration — a find-and-replace across imports — but also the one you do once and forget. The Jakarta namespace is the long-term standard; staying on javax.* indefinitely is a dead end.
Before you upgrade: audit dependencies that use javax.* directly (persistence, servlet, validation) — they need Jakarta-compatible versions.
GraalVM native compilation
Spring Boot 3 has first-class support for compiling your app to a native executable via GraalVM Native Image. The result: dramatically faster startup (milliseconds, not seconds) and much lower memory footprint — relevant for:
- Serverless/FaaS (cold starts kill you on JVM apps)
- Containers where you pay for idle memory
- CLI tools built on Spring
<!-- Add to pom.xml -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
# Build a native executable
./mvnw -Pnative native:compile
# Startup: ~50ms vs 3-8s JVM
./target/my-service
Native compilation runs at build time and requires a GraalVM JDK. Dynamic class loading and reflection need explicit hints (reflect-config.json) — Spring Boot 3 auto-generates most of these via AOT processing, but third-party libraries may need manual hints.
When to use it: serverless, CLIs, memory-constrained containers. For long-running servers where JVM warmup pays off, the JVM still has the edge on peak throughput.
Virtual threads
Spring Boot 3.2+ adds a one-property toggle to run web request handling and @Async tasks on Java 21 virtual threads:
spring:
threads:
virtual:
enabled: true
For I/O-bound services — DB queries, HTTP calls, LLM calls — this gives reactive-level concurrency with simple blocking code and no reactive rewrite. For a deep dive, see Virtual Threads in Spring Boot.
Observability API (Micrometer Observation)
Spring Boot 3 replaced the old @Timed / manual MeterRegistry patterns with a unified Observation API built on Micrometer:
@Service
public class OrderService {
private final ObservationRegistry registry;
public Order process(String id) {
return Observation.createNotStarted("order.process", registry)
.lowCardinalityKeyValue("status", "new")
.observe(() -> fetchAndEnrich(id));
}
}
One Observation call emits a timer metric, a distributed trace span, and an event log — all from a single instrumentation point. This is the right abstraction for LLMops and microservice observability: you get Prometheus metrics, Zipkin/OTLP traces, and logs without instrumenting three separate APIs.
Spring Boot auto-configures it with sensible defaults for HTTP, JDBC, and Spring Data — most instrumentation comes for free on upgrade.
HTTP Interface clients (declarative REST)
Spring Boot 3.1+ ships HTTP Interface clients — a declarative, annotation-driven alternative to RestTemplate / raw WebClient:
@HttpExchange("https://api.partner.com")
interface PartnerClient {
@GetExchange("/orders/{id}")
Order getOrder(@PathVariable String id);
@PostExchange("/orders")
Order createOrder(@RequestBody CreateOrderRequest request);
}
// Register as a bean
@Bean
PartnerClient partnerClient(WebClient.Builder builder) {
WebClient client = builder.baseUrl("https://api.partner.com").build();
return HttpServiceProxyFactory
.builderFor(WebClientAdapter.create(client))
.build()
.createClient(PartnerClient.class);
}
This feels like Spring Data repositories but for HTTP — less boilerplate than WebClient, testable with mocks, and fully compatible with virtual threads (use RestClient adapter for blocking).
Problem Details (RFC 9457)
Spring Boot 3 adds built-in support for Problem Details — the RFC 9457 standard for machine-readable error responses:
// Enable in application.yaml
spring:
mvc:
problemdetails:
enabled: true
With this on, Spring's built-in exceptions automatically produce structured JSON errors:
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "Order 'abc' was not found",
"instance": "/orders/abc"
}
You can extend it with custom ProblemDetail for your own exceptions. No more hand-rolling error response shapes across services.
Migration checklist
- Update Java to 17+ (or 21 for virtual threads) — and verify your runtime, not just build target.
- Rename
javax.*→jakarta.*— your IDE can do a project-wide replace; watch for transitive dependencies. - Upgrade Spring Boot from 2.x → 3.x — follow the official migration guide.
- Check library compatibility — Spring Security 6, Hibernate 6, Flyway 9+ are required.
- Enable virtual threads if on Java 21 — immediate win for I/O-bound services.
- Opt in to Problem Details and Observation API — both are opt-in by default.
FAQ
Is the migration painful?
The namespace change (javax → jakarta) is the only big disruptive step. For most apps, it's a couple hours of search-and-replace plus dependency updates. The payoff is years of support and all the features above.
Do I need GraalVM to use Spring Boot 3? No — GraalVM native compilation is optional. You can run Spring Boot 3 on a standard JVM exactly as before.
What's the minimum Java version? Java 17 for Spring Boot 3.0+; Java 21 for virtual threads (3.2+). Java 21 is the recommended target — it's the current LTS with the most features.
Should I upgrade to 3.0 or jump straight to the latest 3.x? Jump to the latest 3.x patch release directly — there's no value in stopping at 3.0 first.
Spring Boot 3 is the right foundation for the next several years of Java backend development — native images for startup-sensitive workloads, virtual threads for concurrency without complexity, unified observability, cleaner HTTP clients, and standardized error responses. The Jakarta namespace migration is the cost of admission; everything above is the return.
More: virtual threads in depth, modern Java features, Spring AI for enterprise, and microservices best practices.
Modernizing a Spring Boot service and want an architecture review? Let's talk.