Many enterprise systems still run on Spring Boot 2.7 or 3.x. Their teams know the upgrade is due, but it keeps losing against features with a business owner. The calendar is now deciding for them: open-source support for Spring Boot 3.5 ended on 30 June 2026, and for 4.0 it ends on 31 December 2026. This article describes how we upgrade large Spring applications without stopping delivery. Versions and dates are as of October 2026 and come from spring.io and the official migration guides.

Where the support dates stand

From the Spring Boot support calendar:

Line Open-source support until Commercial support until
2.7.x 30 June 2023 (ended) 30 June 2029
3.4.x 31 December 2025 (ended) 31 December 2026
3.5.x 30 June 2026 (ended) 30 June 2032
4.0.x 31 December 2026 31 December 2027
4.1.x 31 July 2027 31 July 2028

Spring's support policy gives each minor release at least 13 months of support, and the last minor of each major line (2.7, 3.5) an additional five years of enterprise support. Commercial support buys time, but it does not remove the work, and the libraries around Spring move on regardless. Our recommended target is Spring Boot 4.1 (4.1.1 at the time of writing): 4.0 leaves open-source support in less than three months.

The incremental path

The official guides are explicit: before moving to 3.0, upgrade to the latest 2.7.x; before 4.0, to the latest 3.5.x. Whatever is deprecated in one line is removed in the next major, so clearing deprecation warnings is part of the upgrade, not cleanup for later.

  1. On 2.7, Java first. Spring Boot 2.7.18 runs on Java 8 and is compatible up to Java 21. Moving to Java 17 or 21 on the current Boot version separates JVM issues from framework issues. Move Spring Security to 5.8, as the 3.0 guide recommends.
  2. 2.7 to 3.5. The real break happens at 3.0: Java 17 baseline, Jakarta EE 10, Hibernate 6, Spring Security 6. From there, move through the minor versions to 3.5, reading each release note. Spring Boot 3.5.16 is compatible with Java 17 to 25.
  3. 3.5 to 4.1. Spring Framework 7, Jakarta EE 11 with Servlet 6.1, Hibernate 7, Jackson 3, modular starters. Spring Boot 4.1.1 requires Java 17 and is compatible up to Java 26. We usually target Java 21 or Java 25; the latter has been generally available since 16 September 2025.

At each step add spring-boot-properties-migrator temporarily: it reports renamed or removed properties at startup and maps them at runtime. Remove it once the warnings are gone.

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

Jakarta EE and other breaking changes

Boot 3 moved from javax.* to jakarta.* packages. Imports are the easy part. The hard part is libraries that ship only javax artifacts, often as transitive dependencies: search the dependency tree for javax.servlet, javax.persistence and javax.validation, and treat every hit as a library to upgrade, replace or isolate. Also look for changes that compile fine but behave differently, such as trailing-slash matching, disabled by default since 3.0: GET /orders/ no longer matches /orders.

Boot 4 brings its own list. The items that usually take time:

  • Jackson 3: packages move from com.fasterxml.jackson to tools.jackson (annotations keep their names) and customizers are renamed. Jackson 2 remains available as a bridge through the deprecated spring-boot-jackson2 module.
  • Modular starters: spring-boot-starter-web becomes spring-boot-starter-webmvc; Flyway and Liquibase need their own starters. spring-boot-starter-classic is an interim option.
  • Removed features: Undertow is gone, since it does not yet support Servlet 6.1.
  • Tests: @MockBean and @SpyBean are replaced by @MockitoBean and @MockitoSpyBean, and @SpringBootTest no longer configures MockMvc without @AutoConfigureMockMvc.

Hibernate 6 and 7: what to check

Hibernate changes rarely break the build. They change SQL and schema behaviour. Spring Boot 3.0 moved to Hibernate 6.1, Boot 4.0 to Hibernate 7.2 with Jakarta Persistence 3.2, and Boot 4.1 ships Hibernate 7.4. From the official migration guides, these deserve attention:

  • Sequences (6.0): implicit generators use one sequence per entity (<entity>_seq) instead of hibernate_sequence, with an allocation size of 50. Existing schemas need new sequences, or a compatibility setting while you plan the change:

    spring.jpa.properties.hibernate.id.db_structure_naming_strategy=legacy
    
  • Types: Instant, UUIDs on MariaDB and SQL Server, and time zone storage are mapped differently. Run schema validation against a copy of production.

  • Fetching (6.0): laziness of associations mapped with FetchMode.JOIN is now respected when loading by id. Expect lazy-loading exceptions or extra queries.

  • API removals (7.0): Session#save, update, saveOrUpdate, delete and load give way to persist, merge, remove and getReference.

  • Stricter validation (7.0): misplaced mapping annotations that used to be ignored are now errors.

We compare generated SQL before and after for critical use cases, and count queries in tests so that N+1 regressions surface before production.

OpenRewrite: an accelerator, not a guarantee

OpenRewrite automates mechanical changes: imports, renamed types, properties, dependency versions. Relevant recipes include UpgradeSpringBoot_3_5 and UpgradeSpringBoot_4_0 in rewrite-spring, and JavaxMigrationToJakarta and UpgradeToJava25 in rewrite-migrate-java. Start with a dry run and review the patch like any other change:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:dryRun \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \
  -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5

Three caveats. Read what a recipe contains: the Spring Boot 4.0 community recipe covers Spring Framework 7, Spring Security 7, Hibernate 7.1, Testcontainers 2 and starter renames, but not a full migration of your code to Jackson 3. Recipes change syntax, not semantics: they will not notice that a query now returns different rows. And check the licensing: the core framework and build plugins are Apache 2.0, but many Spring and migration recipes are under the Moderne Source Available License, which restricts some commercial uses, and recipe artifacts are moving from Maven Central to a repository that requires authentication.

Tests as the safety net

Before changing any version, pin current behaviour with characterization tests: tests that record what the system does today, not what it should do. The most valuable ones sit at the boundaries: REST responses (including JSON serialization, which Jackson 3 can change), published messages, and the rows written by critical transactions.

Run them against real infrastructure with Testcontainers rather than in-memory databases that hide dialect and type differences. On Boot 2.7 you wire container properties with @DynamicPropertySource; since Boot 3.1, @ServiceConnection does it for you:

@Testcontainers
@SpringBootTest
@AutoConfigureMockMvc
class OrderApiCharacterizationTest {

    @Container
    @ServiceConnection
    static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16");

    // record today's responses, assert they are unchanged after each step
}

Boot 4.0 moves to Testcontainers 2.0, whose module artifacts are renamed (for example testcontainers-postgresql), so test code migrates too: change the plumbing, keep the assertions. For APIs consumed by other teams, contract tests protect what you expose.

Dependency and CVE hygiene

An upgrade is also the moment to regain control of dependencies:

  • Let the Spring Boot BOM manage versions; every manual override needs a written reason.
  • Check unmanaged dependencies, such as Spring Cloud, for compatible versions before each step.
  • Identify abandoned libraries early: they are the most common reason an upgrade stalls.
  • Generate an SBOM (CycloneDX) in the build and scan it in CI. Since Spring Boot 3.3, Actuator can expose it through a dedicated endpoint.
  • Keep applying patch releases after go-live: that is where security fixes ship.

Rollout: small steps you can undo

Every step reaches production on its own, with a rollback plan agreed in advance:

  • One change per release: the Java upgrade, a Boot minor version and a Hibernate behaviour change do not ship together.
  • Expand/contract database changes: add new sequences or columns first and remove old ones only when the new version is stable, so the previous release can still run.
  • Canary or blue/green deployment, comparing error rates, latency and query counts between old and new instances. Boot 4 enables liveness and readiness probes by default, which helps on Kubernetes.
  • Feature flags for behaviour you change on purpose during the upgrade, such as a rewritten query.

Checklist

  • Target line chosen and support dates documented
  • Java upgraded separately, on the current Boot version
  • Deprecations removed; properties migrator added, then removed
  • javax dependencies inventoried, Jakarta alternatives identified
  • Hibernate sequences, types and fetching verified on a production-like database
  • Characterization and integration tests green before and after each step
  • OpenRewrite changes reviewed, licence terms checked
  • Release with canary and a tested rollback

Official references: Spring Boot 3.0 migration guide, Spring Boot 4.0 migration guide, Hibernate 6.0 and 7.0 migration guides.

If you need help planning or running the upgrade, see our modernization service and the Spring Modernization Sprint, or contact us.

All insights

Tell us about your system.

Describe the challenge in a few lines: a platform to modernize, an incident that keeps coming back, a security finding, an AI idea. We reply personally, usually within two working days.