Spring Boot has long been the default choice for building production-grade backend development services, and its fourth major generation pushes that further. Spring Boot 4.0 shipped on November 20, 2025, built on top of Spring Framework 7 and Jakarta EE 11, and it's the most significant overhaul of the framework since the Spring Boot 2-to-3 jump that introduced the jakarta.* namespace. A smaller follow-up release, Spring Boot 4.1, arrived in June 2026, bringing gRPC support, security hardening, and further observability improvements.
If you're coming from Spring Boot 3.x, this isn't a routine point upgrade. Spring Boot 4 restructures how the framework is packaged, rethinks null safety across the entire portfolio, and ships genuinely new capabilities, including declarative HTTP clients, first-class API versioning, and a move to Jackson 3, all of which change how you write everyday Spring code. This guide walks through what's new, why it matters in practice, where teams tend to get stuck, and exactly how to move an existing application from 3.x to 4.x.
This guide breaks down Spring Boot 4 through a practical, upgrade-focused lens rather than just a changelog — covering what changed, why it matters, and what actually trips teams up during migration, so you can plan the move with confidence.
Key takeaways
- Learn what's genuinely new in Spring Boot 4 versus a routine version bump.
- Understand how JSpecify null safety changes the way you write everyday Spring code.
- See how native API versioning and declarative HTTP clients cut down on boilerplate.
- Spot the Jackson 3 migration traps before they surface in production.
- Get a step-by-step path for moving an existing Spring Boot 3.x app to 4.x.
- Know which features (Undertow, Jersey) are gone and what to replace them with.

What is Spring Boot 4?
Spring Boot 4.0 is the November 2025 major release of the Spring team's opinionated, auto-configuring application framework for the JVM. It is not an incremental update — it's a new generation built on Spring Framework 7 and Jakarta EE 11, with a completely restructured module layout, a portfolio-wide null-safety standard, and new first-class features for REST API development that previously required third-party libraries or hand-written glue code.
Spring Boot 4.0 requires Java 17 as an absolute minimum, though the project and most migration guides recommend Java 21 or the newly supported Java 25 for better runtime behavior. On the build side, it supports Gradle 8.14+ (Gradle 9 recommended) and current Maven releases.
Exploring Spring Boot 4's new features and their real-world impact
Spring Boot 4 touches nearly every layer of the framework from how jars are packaged, to how nullability is expressed, to how you call other services over HTTP. Below is a deep look at each major feature, what changed from Spring Boot 3.x, and what it looks like in code.
1. Codebase Modularization — Smaller, Focused Jars
In Spring Boot 3.x and earlier, autoconfiguration was largely monolithic: a handful of large starters (spring-boot-starter-web, spring-boot-starter-data-jpa, and so on) pulled in broad, all-in-one classpaths. Spring Boot 4 breaks this apart into smaller, more focused jars, so your application only carries the autoconfiguration classes it actually needs.
Why it matters:
- Smaller jars and leaner classpaths translate into faster startup — meaningful for containers and serverless deployments where cold start time is a cost line item, not just a UX detail.
- Dependency resolution becomes more predictable: fewer surprise transitive classes showing up because one starter quietly pulled in ten others.
- The trade-off is real, though: Spring Boot 3 and Spring Boot 4 dependencies generally shouldn't be mixed on the same classpath. For applications that need a bridge while they migrate, Spring Boot ships a spring-boot-starter-classic (and spring-boot-starter-test-classic for tests) compatibility starter that restores something closer to the old, unified classpath temporarily.
2. JSpecify Null safety — A real standard, not vendor annotations
This is arguably the most consequential change for day-to-day code in Spring Boot 4. For a decade, Spring shipped its own @Nullable / @NonNull / @NonNullApi / @NonNullFields annotations in org.springframework.lang. They were useful as IDE hints but had no formal specification and couldn't express nullability on generics, arrays, or type parameters. As of Spring Framework 7 and Spring Boot 4, those annotations are deprecated in favor of JSpecify — a standard built jointly by Google, JetBrains, and Oracle that every major IDE and static analysis tool can understand natively.
Before (Spring Boot 3.x, package-level annotations):
java
@NonNullApi
@NonNullFields
package com.example.orders;
import org.springframework.lang.Nullable;
Now (Spring Boot 4, JSpecify):
java
@NullMarked
package com.example.orders;
import org.jspecify.annotations.NullMarked;
java
@NullMarked
public class OrderService {
public Order createOrder(String email, @Nullable String promoCode) {
// email is guaranteed non-null by the package's @NullMarked default
// promoCode is explicitly allowed to be null
...
}
}
@NullMarked flips the default for a package, class, or module to "non-null unless marked otherwise" — which matches how most people actually reason about their code. JSpecify also handles nullability on generics, something the old Spring annotations simply couldn't express:
java
// A non-null list that may contain null elements
public void processOptionalTags(List<@Nullable String> tags) { ... }
Migration note: @Nullable on a method's return type is now a type-use annotation, so it moves from above the method signature to directly in front of the return type — a small but easy-to-miss syntax shift when migrating existing code.
3. First-Class API Versioning
Before Spring Boot 4, API versioning had no official answer, teams rolled their own with URL prefixes (/v1/, /v2/), custom HandlerMapping subclasses, or interceptors, and every service ended up doing it slightly differently. Spring Framework 7 adds a version attribute directly to @RequestMapping and its shortcuts (@GetMapping, @PostMapping, etc.), with auto-configuration in Spring Boot 4 for both Spring MVC and WebFlux.
You choose a versioning strategy — URL path segment, request header, query parameter, or media type — with a single property:
properties
# Route by request header
spring.mvc.apiversion.use.header=API-Version
spring.mvc.apiversion.default=1.0
Then declare versions directly on your controller methods:
java
@RestController
@RequestMapping("/accounts")
class AccountController {
@GetMapping(path = "/{id}", version = "1.0+")
AccountV1 getAccountV1(@PathVariable long id) {
return new AccountV1(id, "legacy-name");
}
@GetMapping(path = "/{id}", version = "2.0")
AccountV2 getAccountV2(@PathVariable long id) {
return new AccountV2(id, "name", "email");
}
}
Because the routing strategy lives in configuration rather than in the controller, you can switch strategies without touching controller code—simplifying long-term maintenance in complex systems where APIs and microservices integration form the core architecture. You can also announce a deprecation via the Sunset header effortlessly without modifying a single controller method.
4. Declarative HTTP Service Clients
Spring Framework 6 introduced @HttpExchange interfaces as an alternative to hand-written RestTemplate or WebClient code, but wiring them up still required manually building an HttpServiceProxyFactory and registering a bean for every interface. Spring Boot 4 removes that ceremony with the new @ImportHttpServices annotation, which auto-registers your HTTP interfaces as proxy beans.
Before (Spring Boot 3.x, manual WebClient calls):
java
public User getUser(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
.block();
}
Now (Spring Boot 4, declarative HTTP interface):
java
@HttpExchange("/users")
public interface UserClient {
@GetExchange("/{id}")
User fetchUser(@PathVariable Long id);
@PostExchange
User createUser(@RequestBody User user);
@GetExchange
List<User> fetchUsersByType(@RequestParam String type);
}
java
@Configuration
@ImportHttpServices(group = "userApi", types = UserClient.class)
class HttpClientConfig {
}
properties
spring.http.client.service.group.userApi.base-url=https://api.example.com
spring.http.client.service.read-timeout=2s
Spring generates the implementation at runtime, so a service layer just injects UserClient and calls client.fetchUser(id) like any other bean. Teams that previously reached for Spring Cloud OpenFeign for this kind of declarative client now get most of the same ergonomics natively, with tighter integration into Spring Security 7 and no extra dependency.
5. Jackson 3 — A bigger change
Spring Boot 4 moves from Jackson 2 to Jackson 3, and this is one of the changes most likely to bite teams that upgrade without reading the fine print. Instead of a single ObjectMapper bean, Spring Boot now auto-configures format-specific mappers: a JsonMapper for JSON and an XmlMapper for XML.
Before (Spring Boot 3.x):
java
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper().registerModule(new JavaTimeModule());
}
Now (Spring Boot 4):
java
@Bean
public JsonMapper jsonMapper() {
return JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();
}
Defining an ObjectMapper bean alone is no longer sufficient to override the auto-configured mapper — you need a JsonMapper or XmlMapper bean instead. There's also a quieter, more dangerous change: Jackson 2's JsonProcessingException extended IOException, but Jackson 3's JacksonException extends RuntimeException. Any catch (IOException e) block that was silently catching Jackson parse errors will stop catching them after the upgrade — a bug that tends to surface in production rather than in a unit test.
For teams that need a faster interim fix, Spring Boot ships a spring.jackson.use-jackson2-defaults property and a spring-boot-jackson2 compatibility starter to ease the transition.
6. Observability, Security, and Messaging (Spring Boot 4.1)
Spring Boot 4.1, released in June 2026, builds on the 4.0 foundation with a smaller but production-focused set of additions:
- Spring gRPC support for writing and testing gRPC servers and clients, including standalone servers backed by Netty or Servlet-based HTTP/2 integration — no more hand-rolling gRPC wiring.
- InetAddressFilter for HTTP clients, letting both reactive and blocking HTTP clients block outgoing requests to specific addresses — a direct mitigation for SSRF attacks in applications that fetch user-supplied URLs.
- Lazy JDBC connection acquisition — a single property that defers acquiring a database connection until it's actually needed, rather than at the start of every transaction, which is a meaningful win under connection-pool pressure.
- A new @RedisListener annotation, giving Redis messaging the same declarative, annotation-driven model that @KafkaListener has had for years.
- Automatic context propagation for @Async methods, plus expanded OpenTelemetry support and SSL bundle support for OTLP exporters.
- Type-safe property paths in Spring Data, replacing stringly-typed property references with something the compiler can actually check.
Spring Boot 3.x vs. Spring Boot 4: The quick new feature comparison

Why is Spring Boot 4 a genuine Spring Boot framework upgrade and not just a version change?
1. Meaningful Performance Gains
Modularization means smaller jars and less classpath scanning at startup, which pairs well with GraalVM native image support — Spring Boot 4 is fully aligned with GraalVM 24, with enhanced ahead-of-time (AOT) processing. Spring Data now supports AOT Repositories, turning query methods into source code compiled alongside the rest of the application, thereby shrinking the startup memory footprint. For teams running on Kubernetes with aggressive autoscaling, or on serverless platforms where cold start is billed, these aren't cosmetic wins.
2. Fewer Null-Pointer Surprises, Caught Earlier
JSpecify annotations don't change runtime behavior by themselves, but paired with a modern IDE (IntelliJ 2025.3+ understands @NullMarked and @Nullable out of the box) or a tool like NullAway wired into your build, a large class of NullPointerException bugs moves from a 2 a.m. production page to a compile-time warning.
3. Dramatically Less Boilerplate for Service-to-Service Calls
The HTTP Service Client example above isn't a toy case — teams migrating real services report cutting hundreds of lines of RestClient boilerplate, base-URL configuration, and error-handling wrappers down to a single annotated interface per external API.
4. Stronger Security Defaults Out of the Box
The InetAddressFilter SSRF mitigation shipped in Spring Boot 4.1 is a capability Spring Boot 3.x simply doesn't have — and, per the framework's own support policy, never will, since 3.x is in maintenance mode. If your application fetches URLs supplied by users (webhooks, image proxies, link previews), this closes a real attack surface with a single bean rather than a custom implementation.
5. Native API Versioning Removes a Recurring Team Debate
Every team eventually has the "how do we version our REST API" conversation, and historically the answer varied service to service — some used /v1/, some used headers, some mixed both. Native versioning in Spring Boot 4 doesn't force a single answer, but it does mean the mechanism is standardized across a codebase and configurable without touching controller logic.
6. Better Observability Without Extra Wiring
A dedicated spring-boot-starter-opentelemetry starter, automatic context propagation for @Async methods, and SSL bundle support for OTLP exporters mean production-grade tracing requires less manual assembly than it did in Spring Boot 3.x.
7. A Defined, Predictable Support Runway
Spring Boot 3.5.x — the bridge release built specifically to prepare 3.x codebases for 4.0 — is supported through November 2026. That gives teams a concrete window to plan the migration deliberately, rather than being forced into an emergency upgrade when 3.x support lapses.
Revealing Spring Boot 4 Migration quick tips for smooth Boot Development
Before diving into the full step-by-step walkthrough later in this guide, here's the condensed version — the five checks that catch the majority of Spring Boot 4 migration issues before they reach staging.
1. Get Current on Spring Boot 3.5.x First
Don't jump straight from an old 3.x release to 4.0. Upgrade to the latest Spring Boot 3.5.x release and resolve every deprecation warning it surfaces — 3.5 exists specifically to flag everything that changes in 4.0, so this step does most of your discovery work for free.
2. Confirm Your Java and Gradle Versions
Spring Boot 4.0 requires Java 17 at minimum, with 21 or 25 recommended for better runtime behavior. On the build side, confirm you're on Gradle 8.14+ (Gradle 9 recommended) or a current Maven release — update CI toolchains alongside your local environment.
3. Choose Your Modularization Strategy
Decide between spring-boot-starter-classic (a fast bridge that temporarily restores the old, unified classpath) and the new modular starters (a cleaner, more thorough migration) based on how much time your team has. The classic starter buys breathing room; the modular starters are the long-term destination.
4. Migrate ObjectMapper to JsonMapper/XmlMapper
Replace ObjectMapper beans with JsonMapper or XmlMapper beans, since Jackson 3 auto-configuration no longer picks up a plain ObjectMapper override. While you're in there, re-check any catch (IOException e) blocks around Jackson calls — JacksonException now extends RuntimeException, not IOException, so those catches will silently stop firing.
5. Find and Replace Undertow or Jersey
Search your codebase and dependency tree for Undertow or Jersey usage. Both are gone in Spring Boot 4.0 — Undertow entirely, Jersey pending a JAX-RS 4–compatible release — so plan a replacement (typically Tomcat or Netty) as its own testable step before attempting the version bump.
Challenges and Limitations of Spring Boot 4
Spring Boot 4 unlocks real capability, but it also asks something of every team that adopts it. Here are the friction points that show up most often in practice, and how to work around them.
1. Migration Complexity from Older 3.x Releases
Jumping straight from an early Spring Boot 3.0/3.1 codebase to 4.0 compounds every breaking change from the 3.x line into a single upgrade.
- Challenge: Deprecated APIs removed across multiple minor releases all surface at once.
- Solution: Jumping straight from early 3.x releases compounds breaking changes. Aligning this upgrade with established application modernization best practices ensures zero business disruption during framework transitions.
2. The Jackson 2-to-3 Transition Is Easy to Underestimate
The Maven group ID change (com.fasterxml.jackson → tools.jackson) is handled automatically by Spring Boot's BOM, but the JacksonException/IOException hierarchy change is not something the compiler will flag for you.
- Challenge: Code that silently caught Jackson parse errors via catch (IOException e) stops doing so, often surfacing only in staging or production.
- Solution: Grep for Jackson-adjacent IOException catch blocks before upgrading, and add explicit JacksonException handling where needed. Use spring-boot-jackson2 or the spring.jackson.use-jackson2-defaults property if you need a temporary bridge.
3. Learning Curve for JSpecify Null Safety
Moving from Spring's own @Nullable/@NonNullApi to JSpecify's @Nullable/@NullMarked is a genuine mental model shift, not a search-and-replace.
- Challenge: @Nullable becomes a type-use annotation and needs to move position on method return types; generic nullability (List<@Nullable String>) has no equivalent in the old model.
- Solution: Start with a single, high-value package — core business logic or a historically NPE-prone module — add @NullMarked via package-info.java, and let your IDE or a tool like NullAway guide you to the remaining @Nullable cases.
4. Modularization Can Break Assumptions About the Classpath
Code (yours or a third-party library's) that assumed the old, unified classpath — reflectively scanning for classes that used to always be present, for instance — may not find them anymore.
- Challenge: Runtime ClassNotFoundException or missing-bean errors that don't show up until you actually run the modularized application.
- Solution: Use spring-boot-starter-classic as a temporary bridge while you identify exactly which modules your code depends on, then migrate to explicit modular starters once you've mapped the real dependencies.
5. Dropped Servlet Container and Framework Support
Spring Boot 4.0 removes Undertow entirely and drops Jersey support pending a JAX-RS 4–compatible release.
- Challenge: Applications built on either need a replacement before they can move to 4.x at all.
- Solution: Plan a servlet container migration (typically to Tomcat or Netty) as a discrete, testable step before attempting the Spring Boot version bump — don't try to do both at once.
6. Ecosystem and Third-Party Library Catch-Up
Not every library, starter, or in-house internal dependency has been updated for Spring Framework 7 / Jakarta EE 11 yet.
- Challenge: A dependency that imports a now-deprecated Spring class, or hasn't published a Boot 4–compatible release, can block an otherwise-ready migration.
- Solution: Audit dependencies early — including internal, company-maintained starters — and check upstream release notes or GitHub issues before committing to a migration date.
7. Runtime Surprises That Only Show Up in Integration Tests
Several changes are runtime-only and won't be caught by a clean compile: Spring Batch default behavior changes, Spring Data JPA's bootstrap-mode property handling, and Spring Security 7 configuration differences.
- Challenge: These tend to surface in staging rather than in unit tests.
- Solution: Prioritize integration and staging testing over unit tests for this particular upgrade, and specifically exercise batch jobs, JPA repository bootstrapping, and security filter chains before going to production.

A quick step-by-step Spring Boot 4 Migration guide
1. Get Current on Spring Boot 3.5.x First
Before touching the version number, upgrade to the latest 3.5.x release and resolve every deprecation warning it produces. This is the single most important prerequisite — 3.5 exists specifically to tell you what's about to change.
2. Check Your Java and Build Tool Baseline
Confirm Java 17 or later (21/25 recommended) and Gradle 8.14+ (Gradle 9 recommended) or a current Maven version. Update your CI toolchains alongside your local environment.
3. Audit Third-Party and Internal Dependencies
Compare your project's dependency versions against Spring Boot 4's dependency management. Anything not managed by Spring Boot directly — Spring Cloud modules, internal starters, logging integrations — needs individual verification for Spring Framework 7 / Jakarta EE 11 compatibility.
4. Choose a Modularization Strategy
- Fast path: add spring-boot-starter-classic (and spring-boot-starter-test-classic) to temporarily restore the old, unified classpath while you stabilize everything else.
- Clean path: adopt the new, explicit modular starters directly if you have the time for a thorough migration.
5. Migrate Jackson 2 to Jackson 3
Replace custom ObjectMapper beans with JsonMapper or XmlMapper beans. Re-check exception handling that assumed JacksonException extended IOException. Use spring.jackson.use-jackson2-defaults or spring-boot-jackson2 as an interim bridge if needed.
6. Adopt JSpecify Null Safety Incrementally
Replace package-level @NonNullApi/@NonNullFields with @NullMarked in package-info.java. Start with one package, not the whole codebase, and let IDE warnings or NullAway guide the rest.
7. Replace Undertow or Jersey if Present
If your application depends on either, migrate to Tomcat or Netty as a separate, testable step before the version bump.
8. Re-Test Data Access, Batch, and Security Configuration
Specifically exercise Spring Batch jobs, JPA repository bootstrapping, and Spring Security filter chains in a staging environment — these are the areas most likely to break silently.
9. Consider Adopting the New Features, Not Just Fixing Breakages
Once the application compiles and runs on 4.x, consider adopting API versioning for any REST endpoints under active evolution, and replacing hand-written RestTemplate/WebClient code with declarative @HttpExchange interfaces where it reduces real boilerplate.
10. Automate Where You Can
For larger codebases or multiple services, tools like OpenRewrite and the Moderne platform offer composite recipes (for example, UpgradeSpringBoot_4_0) that automate much of the mechanical work — updating imports, renaming properties, applying the Jackson 3 changes — across an entire organization's repositories rather than one project at a time.
Teams that stayed current on Spring Boot 3.x deprecation warnings generally report the 4.0 migration takes one to three days for a mid-sized service; teams migrating from an older, stale 3.x release should budget significantly more time.
Final Thoughts
Spring Boot 4 isn't a routine version bump. It's a foundational release that resets several long-standing assumptions about how Spring applications are packaged, how nullability is expressed, and how much boilerplate REST development requires. Modularization and JSpecify null safety are the changes that ripple furthest through a codebase; native API versioning and declarative HTTP Service Clients are the ones that will most directly reduce the amount of code your team writes and maintains going forward.
The migration is real work, particularly around Jackson 3 and any dependencies on Undertow or Jersey—but for teams already current on Spring Boot 3.5.x, it's a well-defined, well-documented path rather than an open-ended risk. As organizations navigate architectural changes, partnering with an experienced provider of Java application development services ensures codebases remain performant, secure, and fully aligned with modern JVM capabilities. With 3.5.x support running through November 2026, now is the right time to start planning rather than waiting for that runway to run out.
At Kellton, we help run a zero-downtime upgrade on a production system with real traffic, real data, and real deadlines is another. Our backend development team specializes in Spring Boot and Java modernization — from framework upgrades and API versioning strategy to full microservices architecture. Whether you need a second set of eyes on a migration assessment, a Spring Boot framework migration roadmap, hands-on engineering support on Spring Boot 4 development, or a fully managed upgrade, our team delivers the expert guidance and execution to get you there with confidence.
Ready for Spring Boot 4?
Upgrade your applications with expert Spring Boot 4 migration services..

Common Questions on Spring Boot 4 framework migration and upgrade
Q1. What is Spring Boot 4?
Spring Boot 4 is the November 2025 major release of the framework, built on Spring Framework 7 and Jakarta EE 11. It introduces a modularized codebase, JSpecify-based null safety, native API versioning, declarative HTTP Service Clients, and a move to Jackson 3.
Q2. What are the biggest new features in Spring Boot 4?
Modularized starters, JSpecify null safety, first-class API versioning, declarative @HttpExchange HTTP clients via @ImportHttpServices, and Jackson 3 with format-specific JsonMapper/XmlMapper beans. Spring Boot 4.1 adds Spring gRPC support, SSRF mitigation via InetAddressFilter, and a @RedisListener annotation.
Q3. How do I migrate from Spring Boot 3.x to Spring Boot 4?
Upgrade to the latest Spring Boot 3.5.x release and fix all deprecation warnings first, confirm your Java (17+) and Gradle (8.14+) versions, then move to 4.0 — using spring-boot-starter-classic as a bridge if you need extra time to fully adopt the modular starters.
Q4. Is Spring Boot 4 safe for enterprise use?
Yes — Spring Boot 4.0 is a stable, generally-available release, and 4.1 built directly on it with production-focused additions like gRPC support and SSRF protection. The main enterprise consideration is ecosystem readiness: verify third-party and internal libraries have Spring Framework 7–compatible releases before migrating.
Q5. Does Spring Boot 4 still support Undertow or Jersey?
No. Undertow is removed entirely, and Jersey support is dropped pending a JAX-RS 4–compatible release. Applications on either need to migrate to Tomcat or Netty before upgrading.
Q6. What's the single most common migration mistake?
Underestimating the Jackson 2-to-3 change — specifically, that JacksonException now extends RuntimeException instead of IOException, which silently breaks any catch (IOException e) block that was also catching Jackson parse errors.
Q7. Should I upgrade from Spring Boot 3.5 to 4?
If you're already on a recent 3.5.x release with clean deprecation warnings, the migration is generally manageable and the performance, security, and developer-experience benefits are real. Spring Boot 3.5.x support runs through November 2026, so there's a defined window to plan the move rather than an urgent deadline — but waiting until that window closes compounds the amount of change you'll face at once.
Q8. What Java version does Spring Boot 4 require?
Java 17 is the hard minimum. Spring Boot 4 also adds first-class support for Java 25, and most migration guides recommend targeting 21 or 25 rather than staying on the 17 floor.


