Spring Boot 4 & Spring Framework 7 Migration Guide: Every Breaking Change and How to Fix It
Official support for Spring Boot 3.5 is ending in November this year. This is reason enough to switch to Spring Boot 4 and Spring Framework 7. The biggest problem most developers cite right now is that the breaking changes in Spring Boot 4 are not organized in one place for easy access. We have resources from developer teams, the kind of post-mortems that developer teams frequently do to find the code that broke when updating to a new version, a series of articles published on Medium, and a handful of blogs posted on Spring.io. These resources mostly focus on one problem at a time.
So, there’s a real need to list all the breaking changes in one place with examples of migration in actual production environments. In this guide, we will explain every aspect of the migration to Spring Boot 4 and Spring Framework 7, all the breaking changes from production migrations, and steps to fix them.
A Quick Snapshot of Spring Boot 4
| Detail | Value |
| Spring Framework 7.0 GA | November 13, 2025 |
| Spring Boot 4.0 GA | November 20, 2025 |
| Spring Boot 4.1 | June 2026 (gRPC support, hardened security features, improved observability) |
| Spring Boot 3.5.x support ends | November 2026 |
| Minimum Java version | Java 17 (Though Java 25 is recommended as the latest LTS version) |
| Jakarta EE baseline | Jakarta EE 11 |
| Bundled major versions | Spring Security 7, Spring Data 2025.1, and Hibernate 7.1 |
Why This Migration Feels Different From 2-to-3
One dominant and almost all-encompassing change brought by the migration from Spring Boot 2 to Spring Boot 3 is the renaming of the namespace, from javax.* to jakarta.* Because of this sweeping change, every enterprise Java codebase had to go through a mechanical find-and-replace move. The Boot 4 version, sitting on top of Spring Framework 7, made several critical changes in Spring Security, Spring Data, and Hibernate. The teams who migrated to Spring Boot 4 had to run multiple migrations simultaneously, and each with several breaking changes. While that sounds easier, multiple sets of breaking changes were scattered and were often hard to detect.

Breaking Change #1: The CSRF Default of Spring Security 7
We start from here because it has been singled out multiple times by several post-mortems in actual production migrations. This breaking change is a silent one and doesn't announce itself. For authenticating cookie sessions in browsers, CSRF protection was by default integrated in Spring Boot 3, requiring no configuration changes. This default is changed by Spring Security 7. As you upgrade to Spring Security 7, 403 Forbidden can be returned against POST, PUT, DELETE, and PATCH requests. The error slips silently into the system as you find no startup error, the application crashes, or failures in the compilation step. It just silently rejects write operations. Naturally, it remains undetected during the upgrades and is often detected when integration tests fail, or API clients get confused.
As soon as you know the cause, fixing it is simple. All you need to do is configure CSRF handling explicitly for stateless, token-authenticated APIs. Here’s how to do it:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // appropriate for stateless, token-based APIs
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
);
return http.build();
}
Remember, when the upgrading is finished, by only fixing the compile errors, don’t think your migration job is done. Such changes that quietly slip in can only be detected by running an integration test suite against real HTTP calls.
Breaking Change #2: The Jackson 2 to Jackson 3 Transition
Though this change has already been well documented, it involves several things. You don’t need to take it as an emergency because Jackson 2 and Jackson 3 can run simultaneously without any hiccups, though Spring asks you to switch to Jackson 3 when migrating to Spring Boot 4. Here’s a proper configuration path to make it easier:
spring:
jackson:
use-jackson2-defaults: true
By following the above configuration, you can temporarily keep Jackson 2’s default behavior so that when upgrading to Spring Boot 4, the team can gradually accustom themselves to Jackson 3. They can keep Jackson 3 as something to try later on.
Let’s take a look at all the changes that take place after Jackson 3 is adopted.
- Package rename
com.fasterxml.jackson becomes tools. jackson throughout, and there is only one deliberate exception with jackson-annotations that stays under com.fasterxml.jackson.annotation to ensure backward compatibility. This exception is created because both versions share annotations.
- ObjectMapper becomes JsonMapper
Jackson 2's ObjectMapper was mutable, meaning its configuration could be changed after creation, which caused real issues in multithreaded environments. Jackson 3's JsonMapper is immutable and thread-safe, built through a builder pattern instead.
- SerializerProvider is renamed SerializationContext.
Jackson 3 renames the SerializerProvider API to SerializationContext, justifying the newly expanded role beyond serialization.
- Default behavior changes that break tests even irrespective of the logic.
MapperFeature.SORT_PROPERTIES_ALPHABETICALLY is true by default from now on. Because of this change in the default behavior, the test code breaks, particularly when comparing serialized JSON against a raw string. There have also been small shifts in date format, null fields handling, and numeric format. These small changes are also making test comparisons involving character-exact integration fail. These failures have been observed in several real migrations, even in projects where business logic didn’t change at all.
Here’s how a custom serializer migration looks:
// Jackson 2
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class MoneySerializer extends StdSerializer {
// ...
public void serialize(Money value, JsonGenerator gen, SerializerProvider provider) { ... }
}
// Jackson 3
import tools.jackson.databind.ser.std.StdSerializer;
import tools.jackson.databind.SerializationContext;
public class MoneySerializer extends StdSerializer {
// ...
public void serialize(Money value, JsonGenerator gen, SerializationContext context) { ... }
}
If you are using @JsonView, you can leverage a new Jackson 3-based HTTP message converter that offers direct support for serialization hints. The output is better and more concise compared to the mutable-wrapper approach used earlier.
var response = restClient.post()
.uri("http://localhost:8080/create")
.hint(JsonView.class.getName(), Summary.class)
.body(user)
.retrieve()
.body(String.class);
Automation support
In Spring Framework 7, we have OpenRewrite options to help automate the mechanical building blocks of this migration. Instead of doing everything manually, Spring Application Advisor tools are increasingly popular to get the job done.
Breaking Change #3: JSpecify Null-Safety Annotations
Spring Framework 7 has now added first-class null-safety annotations from JSpecify across the entire API surface. Since Spring Boot 3, APIs were left with implicit non-null assumptions. But they are now explicitly annotated, resulting in the return or acceptance of null. Though for pure Java codebases, it does not make a crucial difference, for certain Java-adjacent codebases like Kotlin, which is known for compile-time null-safety annotations, teams are experiencing friction. The same code that produced no compile-time errors due to non-null assumptions now started showing type mismatches with the introduction of the new nullability annotations of Spring Framework 7. The only effective fix for this is auditing the nullability-sensitive code.
Breaking Change #4: Removal of Undertow Support
Undertow support has not been updated for Jakarta EE 11, which is required by Spring Boot 4. Consequently, on Spring Boot 4, the applications that use spring-boot-starter-undertow will fail to start. Applications using Undertow can only mitigate this absence by migrating to the embedded servers that provide this support. You have embedded server options like Tomcat, Jetty, or Reactor Netty. Remember, while migrating to these supported embedded servers, you need to replace the starter dependency and make required configuration changes.
Breaking Change #5: No Support for JUnit 4 Anymore
The support for JUnit 4 and the JUnit Vintage engine has been revoked in this update. Before you upgrade to Spring Boot 4, migrate all continuing JUnit 4 test classes or junit:junit dependencies to JUnit 5 (Jupiter). Since JUnit 5 has been the standard for quite some time, it only affects aged codebases because the accompanying legacy test suites had never been migrated.
Breaking Change #6: Deprecated APIs Are Fully Gone
Since this is a major release, all deprecated APIs from earlier Spring Boot versions have been removed in Spring Boot 4. Here are some of the most common ones that have been removed.
- WebSecurityConfigurerAdapter: It has been deprecated for several years now. Now component-based SecurityFilterChain configuration will replace it.
- RestTemplate auto-configuration: This is no longer available by default. So the auto-configuration is gone, and it needs explicit opt-in configuration.
- @ConfigurationProperties binding to public fields: It is no longer supported. Now properties classes need to use binding based on either standard getter/setter or on a constructor.
- annotation and javax.inject support: It has been removed completely at the Framework level
Breaking Change #7: 70+ Modular JARs
Spring Boot 4 has championed modularity by organizing its internal packages into more than 70 JARs. Thanks to the introduction of these modular JARs, it’s no longer possible to pull in functionalities directly from a broader starter. Now this needs a standalone explicit declaration for the dependency. Soon after upgrading to Spring Boot 4, if you encounter a ClassNotFoundException or NoClassDefFoundError for any package that always worked before the upgrade, the cause is not a bug, but the effect of these modularized JARs. To resolve this, check the newly updated starter and dependency documentation for the corresponding functionality.
What's New in the New Version?
Remember, the breaking changes we explained above are side effects of improvements offered by this new update. So, let’s now have a quick look at these genuine improvements.
- Inbuilt API version control
You no longer need to create URLs or follow traditional header conventions to control REST API versions; they are now handled natively inside.
- Automatic OpenTelemetry trace propagation targeted to declarative HTTP interface clients
In Spring Framework 6, to wire the trace context across service-to-service HTTP calls, Micrometer tracing always needed explicit configuration. Now, in Framework 7 for declarative HTTP clients, this is handled automatically.
- Complete Jakarta EE 11 alignment
With full Jakarta EE 11 alignment, the namespace transition that started from Boot 3 is coming full circle.
- Reduced runtime footprint
Thanks to modular JARs for packages and lightweight default dependencies, applications’ runtime footprint has been reduced.
- More aligned with virtual threads
Since Spring Boot 4 is built on the Java 21 baseline, it is more aligned with virtual threads, the feature that supports high concurrency workloads.
Safe Migration Roadmap to Spring Boot 4 & Spring Framework 7
Experienced Java developers who have already switched to Spring Boot 4 and Spring Framework 7 should follow a sequence of upgrades rather than a direct one-time jump. Here?s a tried and tested upgrade sequence you should follow:
01. First, get fully up to date on Spring Boot 3.5.x.
If you are still sitting on older versions like 2.x or early 3.x, don’t rush to upgrade to 4.0 at one go. First, get fully up to date on the ongoing 3.5.x version, and address all the deprecation warnings that it shows.
2. Upgrade to the baseline JDK.
1Since Java 17 is the bare minimum requirement for Spring Boot 4, upgrade the JDK if you are still stuck on an older version. Upgrading to Java 25 is the best move as it is the latest version.
3. Run the properties migrator and audit configuration.
Don’t assume that after the upgrade, older configuration will be carried over naturally. You should use the properties migrator tool that can effectively detect renamed or removed configuration properties.
24. Begin by setting the Spring Security 7 CSRF default.
As setting the CSRF default behavior often results in almost undetectable failures, you should begin by configuring CSRF behavior for stateless APIs explicitly. This will guarantee dedicated test coverage, leaving no room for accidental discovery of failures at a later stage.
5. Opt for a deliberate Jackson strategy, not the default one.
3Having a well-thought-out Jackson strategy to automate renaming of mechanical packages and APIs is important before you progress from here. The first option is to preserve use-jackson2-defaults: true for the time being and treat transitioning to Jackson 3 as a standalone follow-up task. The second option is to migrate to the full Jackson 3 using OpenRewrite recipes.
6. Search for removed APIs and fix resulting issues.
The upgrade most commonly results in the removal of WebSecurityConfigurerAdapter, Undertow starters, JUnit 4 dependencies, and public-field @ConfigurationProperties binding. Rather than discovering the resulting compile-time errors at different times, conduct a focused search-and-fix operation.
47. Scrutinize Kotlin codebases meticulously.
As the upgrade will bring more accurate null-safety annotations from JSpecify, new type errors will be common, particularly in null-safety-sensitive codebases like Kotlin. This requires more meticulous scrutiny.
8. Run the complete integration test suite.
5Many of the breaking changes following the migration are detected only at runtime. This is why it is important to run the complete integration test suite rather than a build.
9. Go for incremental rollout in production.
Since this version packs major changes across multiple facets such as framework, security, data, and Hibernate, don’t go for a single one-time rollout in production. Instead, roll out incrementally, starting with staging, followed by a canary or blue-green rollout.
6A Complete List of Breaking-Changes
| Change | Category | Fix |
| CSRF is enabled by default | Spring Security 7 | Change CSRF configuration for stateless APIs |
| Jackson package is renamed (com.fasterxml.jackson → tools.jackson) | Jackson 3 | Update the imports, keeping the annotations package unchanged |
| ObjectMapper → JsonMapper | Jackson 3 | Switch to builder-based, immutable configuration |
| SerializerProvider → SerializationContext | Jackson 3 | Rename in custom serializer signatures |
| Default property sorting in alphabetical order | Jackson 3 | Restore the earlier default or update raw-string test comparisons |
| JSpecify null-safety annotations added | Spring Framework 7 | Meticulously audit nullability in codebases, especially in Kotlin codebases |
| Undertow starter support is revoked | HotSpot/Server | Migrate to embedded servers like Tomcat, Jetty, or Reactor Netty |
| JUnit 4 support is revoked | Testing | Migrate to JUnit 5 |
| WebSecurityConfigurerAdapter is removed | Spring Security | Use SecurityFilterChain component configuration |
| RestTemplate auto-configuration is removed | Spring Boot | Configure explicitly when required |
| Public-field @ConfigurationProperties binding is removed | Spring Boot | Use getter/setter or constructor binding |
| javax.annotation / javax.inject is removed | Jakarta EE 11 | Migrate to equivalents of jakarta.* |
| Modular 70+ JARs for packages | Dependency management | Look for any transitive dependency missing after the upgrade |
Final Thoughts
Spring Boot 4 and Spring Framework 7 upgrade is massive because it touches every facet of development, framework, packages, data, security, Hibernate, and almost everything in between. With so many changes bundled together, failures and breakups are very likely to occur during upgrades. But practitioners who follow the incremental migration path with preparatory steps we mentioned in the migration roadmap are likely to feel less hackled.
Related Tutorials