Back to blog

// OSSeva Blog

Migration

Spring Boot 3.5 to 4 Upgrade Guide: Breaking Changes and How to Find Each

Matt Reynolds11 min read

The short answer

Upgrade to the latest 3.5 release first, then move to Spring Boot 4.1 rather than 4.0. Java 17 is still the minimum, so most teams do not need a new JDK. The work is in five places: dependencies, because Boot 4 ships smaller modules and some technologies now need their own starter; JSON, because Jackson 3 changes group IDs, package names and several Boot class names; tests, because @MockBean is gone and @SpringBootTest no longer sets up MockMvc on its own; servers, because Undertow is no longer supported and external containers must implement Servlet 6.1; and everything deprecated in 3.x, which has been removed.

Spring Boot 3.5 left open source support on 30 June 2026. Spring Boot 4.0 leaves it on 31 December 2026, and 4.1 is supported until 31 July 2027, so 4.1 is the target that buys time.

Supported upgrade paths

FromToGuidance
Latest 3.5.x (3.5.16)4.0.x or 4.1.xThe path the migration guide describes. Start from the newest 3.5 release.
3.0 to 3.44.xMove to 3.5 first. The 4.0 release notes strongly recommend it.
2.7 or earlier4.xDo the javax to jakarta move and the 3.x upgrade first. See the Spring Boot 2 to 3 migration guide.
4.0.x4.1.xA minor upgrade on the same Spring Framework 7.0 line. Classes, methods and properties deprecated in 4.0 are removed.

Support dates that set the deadline

LineOpen source support endsCommercial support endsLatest release
3.530 June 202630 June 20323.5.16
4.031 December 202631 December 20274.0.8
4.131 July 202731 July 20284.1.1
4.231 December 202731 December 2028Milestones; spring.io lists the first release for November 2026

Commercial support means Broadcom's paid Spring releases. The dates are on the Spring Boot 3.5 end of life and Spring Boot 4.0 end of life pages.

Pre-upgrade checklist

  • Every application builds on Spring Boot 3.5.16 with deprecation warnings visible, and the warnings are cleared.
  • JDK 17 or later in the build and at runtime. Kotlin projects on 2.2 or later. Native images built with GraalVM 25 or later.
  • Gradle 8.14 or later, or Gradle 9.
  • For war deployments, the target container implements Servlet 6.1, such as Tomcat 11.0 or Jetty 12.1.
  • Every dependency Boot does not manage, Spring Cloud being the usual one, has a release that supports Boot 4.
  • A list of the Jackson features, custom serializers and ObjectMapper beans each service relies on.
  • The 3.5 artifact for each service kept in your repository manager, so the previous build can be redeployed.

Breaking change 1: the platform baseline moved

Spring Boot 4 is built on Spring Framework 7, Jakarta EE 11 and Servlet 6.1, and manages Tomcat 11.0 and Jetty 12.1. Java 17 remains the minimum; Spring Framework 7 recommends JDK 25. If you manage Spring dependencies yourself, you must move to Spring Framework 7.x.

Spring Framework 7 no longer supports annotations from the javax.annotation and javax.inject packages, such as @javax.annotation.PostConstruct, @javax.annotation.Resource and @javax.inject.Inject. If the old jars are still on the classpath the code compiles, but Spring no longer acts on those annotations, so the failure shows up at runtime. Find them before the upgrade:

grep -rnE "import javax\.(annotation|inject)\." src/

Replace them with the jakarta.annotation and jakarta.inject equivalents. Our javax to jakarta migration post covers the wider namespace change.

Breaking change 2: Undertow is no longer supported

Undertow does not yet implement Servlet 6.1, so Spring Boot 4 removed the Undertow starter and the option to run Undertow as the embedded server. Spring Framework 7 also removed its Undertow-specific WebSocket and WebFlux classes. Check each build:

mvn dependency:tree | grep -i undertow
./gradlew dependencies --configuration runtimeClasspath | grep -i undertow

Applications that used Undertow need to move to Tomcat, Jetty or Reactor Netty, and any Undertow-specific tuning in server.undertow.* properties has to be redone for the new server.

Breaking change 3: modules and starters were split up

Spring Boot 4 ships smaller modules named spring-boot-<technology>, each with its own root package org.springframework.boot.<technology> and a matching starter. Two things break:

  • Technologies that used to work with only the third-party jar. Flyway and Liquibase are the common cases: the application now needs spring-boot-starter-flyway or spring-boot-starter-liquibase, or the auto-configuration is missing and migrations do not run at startup.
  • Imports of auto-configuration classes. Packages moved with the modules. For example, @EntityScan is now in org.springframework.boot.persistence.autoconfigure.

Several starters were renamed. The old names still work in 4.0 but are deprecated:

Old starterNew starter
spring-boot-starter-webspring-boot-starter-webmvc
spring-boot-starter-web-servicesspring-boot-starter-webservices
spring-boot-starter-oauth2-clientspring-boot-starter-security-oauth2-client
spring-boot-starter-oauth2-resource-serverspring-boot-starter-security-oauth2-resource-server
spring-boot-starter-aopspring-boot-starter-aspectj (check whether you use AspectJ at all)

Test infrastructure follows the same pattern, with a spring-boot-starter-<technology>-test companion for each starter. For example, @WithMockUser now needs spring-boot-starter-security-test to work properly.

To get an application compiling quickly, the migration guide offers spring-boot-starter-classic and spring-boot-starter-test-classic, which put all modules back on the classpath. Use them as an intermediate step: fix imports with the classic starters in place, then remove them and add the specific starters the build reports as missing.

Breaking change 4: Jackson 3 is the default

Spring Boot 4 prefers Jackson 3, which moves from the com.fasterxml.jackson group and packages to tools.jackson. The exception is jackson-annotations, which keeps the com.fasterxml.jackson.annotation package, so annotations on your DTOs do not change. Find the code that does:

grep -rnE "com\.fasterxml\.jackson\.(databind|core|datatype|dataformat)" src/
grep -rnE "@JsonComponent|@JsonMixin|Jackson2ObjectMapperBuilderCustomizer|ObjectMapper objectMapper\(" src/

What changes with it:

  • @JsonComponent is now @JacksonComponent and @JsonMixin is now @JacksonMixin. Jackson2ObjectMapperBuilderCustomizer becomes JsonMapperBuilderCustomizer.
  • Boot auto-configures a JsonMapper and an XmlMapper. Declaring an ObjectMapper bean no longer replaces them; declare a JsonMapper bean instead.
  • spring.jackson.read.* and spring.jackson.write.* moved to spring.jackson.json.read.* and spring.jackson.json.write.*.
  • Jackson now registers every module it finds on the classpath, not only well-known ones. Set spring.jackson.find-and-add-modules=false to keep the old behaviour.

Jackson 3 also changed defaults. Setting spring.jackson.use-jackson2-defaults=true brings the auto-configured mapper as close as possible to Spring Boot 3.x behaviour, which matters when other services, caches or message queues read the JSON you write. For libraries that still need Jackson 2, Boot keeps dependency management for it, and a deprecated spring-boot-jackson2 module exists as a stop-gap. Jersey is one such case: Boot 4.0 supports Jersey 4.0, which does not yet support Jackson 3.

Breaking change 5: tests lose implicit setup

  • @MockBean and @SpyBean were removed. Use @MockitoBean and @MockitoSpyBean.
  • @SpringBootTest no longer provides MockMvc. Add @AutoConfigureMockMvc.
  • @SpringBootTest no longer provides TestRestTemplate or WebClient beans. Add @AutoConfigureTestRestTemplate plus the spring-boot-resttestclient and spring-boot-restclient dependencies, or move to the new RestTestClient with @AutoConfigureRestTestClient.
  • MockitoTestExecutionListener, deprecated in 3.4, is gone. Fields annotated with Mockito's @Mock or @Captor need MockitoExtension.
grep -rlE "@MockBean|@SpyBean" src/test | wc -l
grep -rlE "MockMvc" src/test | xargs grep -L "AutoConfigureMockMvc"

Breaking change 6: renamed properties and changed behaviour

Add the properties migrator for the first run on Boot 4. It prints every renamed or removed property it finds at startup and maps them temporarily:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
  <scope>runtime</scope>
</dependency>

Remove it once the configuration is updated. The changes most likely to matter:

  • MongoDB connection properties such as spring.data.mongodb.uri, host and database moved to spring.mongodb.*.
  • spring.session.redis.* is now spring.session.data.redis.*.
  • spring-boot-starter-batch now runs Spring Batch without a database. An application that upgrades without changing its starter stops writing job metadata to the existing tables, so job history and restart data no longer persist. Use spring-boot-starter-batch-jdbc to keep the database-backed job repository.
  • Liveness and readiness probes are on by default, so the health endpoint exposes those groups.
  • Optional Maven dependencies are no longer packaged into uber jars, and the classic uber-jar loader and the embedded launch scripts for fully executable jars were removed.
  • War deployments to Tomcat need spring-boot-starter-tomcat-runtime instead of spring-boot-starter-tomcat.

Also removed in 4.0: Spring Session Hazelcast and Spring Session MongoDB support, now maintained by the Hazelcast and MongoDB teams, auto-configuration for the reactive Pulsar client, and Spock integration, which 4.1 restores.

If you go straight to 4.1

Spring Boot 4.1 removes what 4.0 deprecated, so the deprecated starter names and the Jackson 2 stop-gaps are worth leaving behind during the move rather than after it. 4.1 also removes the layertools jar mode in favour of tools, deprecates Apache Derby support, and stops treating -DskipTests as a reason to skip test AOT processing in the Maven plugin; use maven.test.skip. Its managed jOOQ version, 3.20, needs Java 21. It stays on Spring Framework 7.0 and moves Spring Security to 7.1.

An order of work that keeps a way back

  1. Move every service to 3.5.16, build with deprecation warnings on, and clear them.
  2. Replace javax.annotation and javax.inject imports, and swap Undertow for Tomcat or Jetty while still on 3.5.
  3. Upgrade one service to Boot 4.1 with the classic starters and the properties migrator. Get it compiling and its tests passing.
  4. Replace the classic starters with specific ones, paying attention to Flyway, Liquibase, Batch and security test starters.
  5. Move JSON code to Jackson 3. Where other systems consume the output, compare serialized payloads from the 3.5 and 4.1 builds before release, and use spring.jackson.use-jackson2-defaults where they differ.
  6. Remove the properties migrator, deploy to a canary or a single instance, and watch health, startup logs and error rates.
  7. Roll out the remaining instances, then the remaining services in dependency order.

Rollback limits. Rolling back an application is a redeploy of the 3.5 artifact, which is why step 1 matters: keep that build current until 4.1 has run in production for a while. Rollback does not undo what the new version wrote. Check three things before calling it safe: JSON written by Jackson 3 to caches, queues or other services, Spring Batch job metadata if the starter changed, and any database migrations Flyway or Liquibase applied on first start.

Where OSSeva fits

Not every application can move before its line runs out. OSSeva for Spring Boot ships patched, signed artifacts for Spring Boot 3.5, which is already past open source support, and for 4.0 after 31 December 2026, delivered through your own Maven or Gradle repository manager, so the 4.1 upgrade can follow your release calendar rather than spring.io's. OSSeva Assure adds an annual architecture review, Java version sequencing and Spring Security reconfiguration guidance. Book a discovery call for a quote. If you are comparing vendors for the gap, see Spring end-of-life support providers.

Frequently asked questions

Does Spring Boot 4 require Java 21?

No. Spring Boot 4.0 and 4.1 require Java 17 or later, and the migration guide encourages the latest LTS. Some managed libraries ask for more: jOOQ 3.20 in Boot 4.1 needs Java 21.

Can I upgrade from Spring Boot 3.3 directly to 4.0?

The release notes strongly recommend upgrading to 3.5 first. 3.5 shows deprecation warnings for everything 4.0 removes, which is the cheapest way to find the code that will break.

Should I upgrade to Spring Boot 4.0 or 4.1?

4.1. Open source support for 4.0 ends on 31 December 2026, and 4.1 is supported until 31 July 2027 on the same Spring Framework 7.0 line.

Why does my Flyway migration not run after upgrading to Spring Boot 4?

Flyway auto-configuration moved into its own module. Add spring-boot-starter-flyway; the same applies to Liquibase with spring-boot-starter-liquibase.

Can I keep using Jackson 2 with Spring Boot 4?

For a transition, yes. Boot keeps dependency management for Jackson 2 and ships a deprecated spring-boot-jackson2 module, but it will be removed in a future release, so plan the move to Jackson 3.

Does Spring Boot 4 support Undertow?

No. Undertow does not yet support Servlet 6.1, so the starter and embedded server support were removed. Use Tomcat, Jetty or Reactor Netty.

What replaces @MockBean in Spring Boot 4?

@MockitoBean, and @MockitoSpyBean replaces @SpyBean.

Tags

Spring BootSpring Boot 4UpgradeJackson 3Jakarta EE

Ready to get your open source under control?

Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.