From Java 21 to 25: Full Guide to Upgrading Spring Boot 3.5 → 4.0
This article explains why staying on old Java and Spring Boot versions is risky, introduces a dual‑axis model for the upgrade, details Java 21‑to‑25 language and JVM improvements, outlines Spring Boot 3.5‑to‑4.0 structural changes, and provides a step‑by‑step manual and OpenRewrite migration workflow with verification guidelines.
Dual‑axis model
The upgrade consists of two independent axes:
Language & JVM: Java 21 → 25 (syntax, GC, scheduling, observability)
Framework ecosystem: Spring Boot 3.5 → 4.0 (dependency baseline, configuration model, starter structure)
Treating them as a single change hides risk; splitting them into two phases makes risk controllable.
Part I – Java 21 → 25 – engineering‑level optimisations
Pattern matching becomes first‑class
if (obj instanceof User u) {</code><code> process(u);</code><code>}Reduces boilerplate, eliminates cast errors, and clarifies control flow in controllers, listeners, and validation logic.
Switch expressions for business rules
return switch (status) {</code><code> case CREATED -> HttpStatus.CREATED;</code><code> case UPDATED -> HttpStatus.OK;</code><code> case DELETED -> HttpStatus.NO_CONTENT;</code><code>};Enforces exhaustiveness, removes fall‑through, and makes rules explicit, improving correctness in service layers.
Records as default data carriers
public record CreateUserRequest(String name, String email) {}Immutable, thread‑safe, fully supported by Jackson and validation, ideal for REST and MQ payloads.
Virtual threads mature
Java 21 introduced virtual threads; Java 25 refines scheduling, lowers memory use, and reduces edge‑case bugs. Traditional Spring MVC code can gain massive concurrency without rewriting to reactive.
GC and observability improvements
Optimised GC tuning strategies
More precise JFR events
Easier stack analysis and clearer memory diagnostics
These changes are invisible in code but reduce production incidents.
Deprecated API phase‑out
Warnings become stronger
IDE hints clearer
APIs eventually removed
Incremental upgrades keep diffs small; staying long leads to massive refactoring.
Part II – Spring Boot 3.5 → 4.0 – ecosystem baseline reset
Dependency baseline shift
All transitive dependencies are upgraded, old overrides stop working, and half‑upgrades are impossible. This yields fewer dependency drifts, a cleaner classpath, and a more consistent ecosystem.
Starter granularity
Starters are split into finer modules, providing precise dependencies and faster startup at the cost of short‑term inconvenience.
Stricter configuration – Fail Fast
# old</code><code>spring.http.client.connect-timeout=5s # new</code><code>spring.http.clients.connect-timeout=5sBoot 4 aborts startup on misconfiguration, preventing hidden production errors.
Serialization changes
JSON field order
Default null handling
Validation error structure
Requires contract tests and separate API refactoring from the upgrade.
Observability assumptions
Metrics must exist
Tracing must exist
Logs must be structured
Boot 4 targets production systems rather than demos.
Part III – Safe migration workflow
Recommended migration order
Upgrade Java 21 → 25
Ensure all tests pass
Manually change Boot version
Verify application starts
Run OpenRewrite for Boot 4
Run regression tests
Canary release
Each step is reversible.
Manual migration steps
Step 1 – Lock Java 25
<properties></code><code> <maven.compiler.source>25</maven.compiler.source></code><code> <maven.compiler.target>25</maven.compiler.target></code><code></properties>Use Maven Enforcer to avoid local JDK mismatches.
Step 2 – Change Spring Boot version
<properties></code><code> <spring-boot.version>4.0.0</spring-boot.version></code><code></properties>A large diff indicates existing dependency drift.
Step 3 – Fix issues in order
Compilation errors
Dependency conflicts
Unit test failures
Startup configuration
Runtime behaviour
Do not skip steps.
OpenRewrite automation
Core recipes: org.openrewrite.java.migrate.UpgradeToJava25 – updates build files, removes deprecated APIs, cleans old SecurityManager, updates I/O patterns. org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 – migrates Spring Boot code.
Plugin configuration excerpt:
<plugin></code><code> <groupId>org.openrewrite.maven</groupId></code><code> <artifactId>rewrite-maven-plugin</artifactId></code><code> <version>7.24.0</version></code><code> <configuration></code><code> <activeRecipes></code><code> <recipe>org.openrewrite.java.migrate.UpgradeToJava25</recipe></code><code> <recipe>org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0</recipe></code><code> </activeRecipes></code><code> </configuration></code><code></plugin>Run:
mvn rewrite:dryRun</code><code>mvn rewrite:run</code><code>mvn clean compile</code><code>mvn testVerification after upgrade
Compare production metrics such as error rate, p95 latency, GC behavior, and downstream call failure rate. An upgrade without data is speculation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
