Skip to main content

Spring Boot 3.x Is EOL. Don’t Stop at 4.0. Target 4.1 Instead.

Spring Boot 3.x is now outside OSS support. Here’s how to calculate the risk, choose the right 4.x landing zone, automate the mechanical work with OpenRewrite, and complete the migration without turning it into a six-month rewrite.

Spring Boot 3.x is now outside OSS support. Here’s how to calculate the risk, choose the right 4.x landing zone, automate the mechanical work with OpenRewrite, and complete the migration without turning it into a six-month rewrite.

If you’re still running Spring Boot 3.x in August 2026, the question is no longer whether to migrate. It’s where to land.

Spring Boot 3.5’s final OSS release, 3.5.16, shipped on June 25, 2026, and open-source support ended five days later. Meanwhile Spring Boot 4.0’s OSS window closes December 31, 2026, while 4.1 runs through July 31, 2027.

So “upgrade to Spring Boot 4” is directionally correct but operationally incomplete. If you’re starting this migration now, use 4.0 as a waypoint and the latest 4.1.x as your deployment target. That distinction is what this article is about.

If I Were Starting This Migration Today

Your situation My recommendation
Boot 3.0 – 3.3 Start immediately; prioritize internet-facing services
Boot 3.4 Start immediately; don’t spend time making 3.4 “clean” first
Boot 3.5 Upgrade now; don’t wait for another maintenance cycle
Boot 4.0 Move to the latest 4.1.x this quarter
Boot 4.1.x Stay current on the latest 4.1 patch
Java below 17 Treat JDK modernization as a prerequisite, not part of this migration
Undertow Plan the container migration before the Boot migration
Large monorepo Migrate service by service, one branch each

My default target in August 2026: the latest Spring Boot 4.1.x, not 4.0.x.

The Numbers That Matter

Date Event
Nov 20, 2025 Spring Boot 4.0 released
Jun 10, 2026 Spring Boot 4.1 released
Jun 25, 2026 Boot 3.5.16 — the final 3.x OSS patch
Jun 30, 2026 Boot 3.5 OSS support ended (last 3.x branch)
Aug 20, 2026 Boot 4.1.1 and 4.0.8 released — current patches
Dec 31, 2026 Boot 4.0 OSS support ends
Jul 31, 2027 Boot 4.1 OSS support ends

Release dates from the Spring team’s announcement posts; support-window dates from the Spring Boot support page and the endoflife.date tracker. Note that endoflife.date normalizes release dates to month-end, so you’ll see 4.1 listed there as June 30 rather than the announced June 10.

Spring Boot Support Reality — August 2026

Version OSS status Final / current OSS patch Your situation
3.0 🔴 EOL (Dec 2023) 3.0.13 Upgrade now
3.1 🔴 EOL (Jun 2024) 3.1.12 Upgrade now
3.2 🔴 EOL (Dec 2024) 3.2.12 Upgrade now
3.3 🔴 EOL (Jun 2025) 3.3.13 Upgrade now
3.4 🔴 EOL (Dec 2025) 3.4.13 Upgrade now
3.5 🔴 EOL (Jun 2026) 3.5.16 Upgrade now
4.0 🟠 Supported to Dec 2026 4.0.8 Don’t stop here
4.1 🟢 Supported to Jul 2027 4.1.1 Recommended target

One distinction people miss: OSS support and commercial support are separate clocks. Boot 3.5 has paid coverage available through 2032. “We’re on a supported version” can therefore be true for a vendor and false for your Maven Central builds simultaneously. If nobody in your organization has bought that coverage, the OSS column is the one that governs you.

How Much Runway Are You Actually Buying?

Starting from August 22, 2026:

Boot 3.x   ####################   EOL - no OSS runway

Boot 4.0   ######                 -> Dec 31, 2026   (~4 months)

Boot 4.1   ################       -> Jul 31, 2027   (~11 months)

The important number is not the major version. It is remaining supported runway. The two hops are not equal in effort — 3.x to 4.0 is the substantial one, and 4.0 to 4.1 is comparatively small — but the incremental cost of continuing to 4.1 is a fraction of the first hop, and it roughly triples the runway you get for the whole exercise.

Waypoint versus deployment target

                THE MIGRATION                  THE DEPLOYMENT
                (automated + manual)           (what runs in prod)

 Boot 3.x  ---------->  Boot 4.0  ---------->  Boot 4.1.x  ---->  PRODUCTION
           large hop    ^^^^^^^^   small hop   ^^^^^^^^^^
           (recipe +    WAYPOINT               TARGET
            manual)     - never deployed       - deployed
                        - not soaked           - soaked + verified
                        - not a release        - the actual release
                        - expires Dec 2026     - supported to Jul 2027

Passing through 4.0 is unavoidable: it is where the automation lands, and 4.1 is a minor release on top of it. Stopping there is the avoidable part.

Why EOL Changes the Risk Model

Be precise about what happened on June 30. EOL does not mean the software suddenly became vulnerable. It means you lost the upstream mechanism for receiving fixes. Those are different claims, and conflating them is how these articles lose credibility with the people who most need to read them.

The difference is structural:

SUPPORTED BRANCH              EOL BRANCH
      |                             |
New vulnerability             New vulnerability
      |                             |
Upstream fix                  Upstream fix exists
      |                       (on a newer branch)
New patch release                   |
      |                       No OSS patch for your branch
You upgrade                         |
      |                       Backport / commercial support / upgrade
   Resolved                         |
                                 Your problem

There’s a second, subtler effect. Steve Poole’s “zombie dependency” analysis makes the case that the CVE reporting pipeline winds down for EOL branches too: researchers redirect attention to supported versions, maintainers stop triaging against the old branch, and fewer identifiers get assigned for code nobody plans to patch. If that holds, a quiet scanner on an EOL branch is weak evidence of safety rather than good news. It’s an argument from observed industry pattern rather than a controlled study — but the incentive structure behind it is hard to dispute.

This Is Not Hypothetical: An Advisory From Two Days Ago

On August 20, 2026 — eight weeks after Boot 3.5 went EOL, and two days before this article was published — Spring released a large batch of advisories across the portfolio: Spring Security, Spring Data JPA, Spring Data REST, Spring Cloud Config, Spring Integration, Spring AMQP, Spring Batch, Spring AI, Reactor Core and Reactor Netty all appear in it.

Take one: CVE-2026-47841, rated HIGH — a WebAuthn user-verification bypass in Spring Security. Spring Security compares UserVerificationRequirement by identity (==). When an HTTP session is serialized and deserialized through a distributed store, the deserialized instance is no longer the same object reference as the static REQUIRED constant, the comparison evaluates false, and the user-verification requirement is silently disabled. An attacker holding a user’s authenticator can then complete authentication without the PIN or biometric step the relying party explicitly required.

It applies when all three of these are true: the application uses WebAuthn (passkey) authentication; it explicitly configures userVerification = REQUIRED (the PREFERRED default is unaffected); and it uses a distributed session store such as Spring Session with Redis, JDBC, or Hazelcast. Narrow conditions — but a bank, a healthcare portal, or anything else that deliberately hardened its passkey flow to REQUIRED and runs more than one replica meets all three.

Here is its published fix matrix:

Affected Spring Security Ships with Fix version Availability
7.1.0 Boot 4.1.x 7.1.1 OSS (free)
7.0.0 – 7.0.6 Boot 4.0.x 7.0.7 OSS (free)
6.5.0 – 6.5.11 Boot 3.5 6.5.12 Enterprise Support only (paid)
6.4.0 – 6.4.18 Boot 3.4 6.4.19 Enterprise Support only (paid)

Read that fourth column again

Both supported Boot 4 lines received a free fix. Both EOL Boot 3 lines did not. This is the mechanism described above, playing out in real time on a HIGH-severity authentication bypass, eight weeks after the support window closed — not a projection about what might happen someday. If you are on Boot 3.5 with passkeys and a Redis session store, the remediation available to you today is a paid subscription or an upgrade.

The Same Pattern in June, and How Not to Over-Read It

On June 8, 2026, Spring published a batch of Framework advisories. The most severe, CVE-2026-41842, is rated HIGH: Spring MVC and WebFlux applications serving static resources from the file system with versioned-resources support configured can be driven into a Denial of Service by requests that are slow to resolve and hold HTTP connections open. Its fix matrix shows the same split two months earlier, when 3.5 was still inside its window by three weeks — which is what makes the August advisory above the more instructive of the two.

Affected Framework range Runs under Fix version Availability
7.0.0 – 7.0.7 Boot 4.0.x 7.0.8 OSS (free)
6.2.0 – 6.2.18 Boot 3.5 6.2.19 OSS (free — 3.5 was three weeks from EOL)
6.1.0 – 6.1.27 Boot 3.3 – 3.4 6.1.28 Enterprise Support only (paid)
5.3.48 and earlier Boot 2.x era 5.3.49 Enterprise Support only (paid)

Three sibling advisories shipped the same day: CVE-2026-41844 (open redirect via an unscoped "/**" mapping and the redirect: prefix), CVE-2026-41849 and CVE-2026-41850 (both SpEL-related DoS).

Don’t overgeneralize this

It would be sloppy to conclude “EOL means every future CVE is unpatchable for you.” The honest framing separates three different questions: whether a vulnerability exists, whether your application is affected, and whether your dependency line will receive an OSS fix.

For any advisory, work through: (1) Is my dependency version in the affected range? (2) Is the vulnerable code path reachable in my application — in the CVE above, do I actually serve file-system static resources with versioned-resources configured? (3) Is a compatible OSS fix published for my dependency line? (4) If not, can I upgrade that dependency independently without breaking the Boot dependency graph? A Jackson or Hibernate CVE can often be resolved by bumping that library alone; a Spring Framework CVE usually cannot, because the Boot BOM pins it.

What EOL does is make question (3) progressively dangerous. Eventually the answer becomes “not from upstream OSS,” and question (4) becomes your only lever — on the component where it works least well.

Why 4.1, Not 4.0

What Spring publishes

Boot 4.0 was released November 20, 2025; its OSS support ends December 31, 2026. Boot 4.1 was released June 10, 2026; its OSS support ends July 31, 2027, with commercial support to July 2028. Classes, methods and properties deprecated in 4.0 have been removed in 4.1, including the layertools jar mode and the -DskipTests Maven flag behaviour for AOT test processing. 4.1 adds gRPC auto-configuration via Spring gRPC 1.1, HTTP-client SSRF mitigation, Kotlin 2.3 with Kotlin Serialization 1.11, lazy datasource connections, async context propagation for @Async methods, and further OpenTelemetry work. 4.1 supports Java 17 through 26; 4.0 supports 17 through 25. Apache Derby support is deprecated in 4.1 following the Derby project’s retirement.

What that means for a team migrating in August 2026

Landing on 4.0 buys about four months of OSS runway and guarantees a second migration inside the same planning year — because the deprecations 4.1 removed will need addressing whenever you make that hop anyway. Doing both hops back to back, while the code and the context are fresh, is cheaper than scheduling them separately. My recommendation: don’t put a new production deployment on 4.0 in August 2026 without a specific reason to. That’s an opinion about the calendar, not an official Spring position.

When Boot 4.0 Is Still the Right Choice

The counterargument deserves airtime, because 4.0 is not a bad release. I’d choose it deliberately if:

  • a dependency you rely on isn’t ready for 4.1 yet;
  • your production certification or compliance baseline explicitly targets 4.0;
  • your vendor supports only the 4.0 line;
  • you’re running a staged migration and 4.0 is an intentional intermediate production release;
  • a change freeze makes the 4.1 hop impossible before the 4.0 window closes — in which case plan the hop for the moment the freeze lifts;
  • you hold commercial support covering the 4.0 lifecycle, which extends to December 2027.

4.0 isn’t a bad release. It’s a short-lived target in the calendar we’re currently in.

What About 4.2?

Reasonable question, since Spring Boot 4.2.0-M1 is already published and 4.2 GA is expected around November 2026 on the usual roughly six-month cadence. If 4.1 expires in July 2027, why not wait for or target 4.2 and buy even more runway?

Two reasons not to, for a migration starting now:

  • Milestones are not production releases. M-builds exist to gather feedback; their APIs can still move before GA, and they carry no support commitment at all. Migrating a production service onto a milestone trades a known expiry date for an unknown API surface — a worse deal than the one you are trying to escape.
  • Waiting for 4.2 GA means staying on an EOL branch for another three months or so. The August advisory above is what that costs. “Skip 4.1, wait for 4.2” is really “remain unpatched until November, then attempt a larger migration under more time pressure.”

The pattern generalizes: migrate to the newest GA release, not the newest release. Once you are on 4.1.x, adopting 4.2 later is a minor-version hop of the same size as 4.0 to 4.1 — the expensive part, the major-version jump, is already behind you. That is the actual dividend of getting onto the 4.x line properly the first time.

Decision Tree

                  Are you on Boot 3.x?
                          |
              +-----------+-----------+
              |                       |
             No                      Yes
              |                       |
      Already on 4.0?           Is Java >= 17?
              |                       |
         Plan 4.1 hop       +---------+---------+
         this quarter       |                   |
                           No                  Yes
                            |                   |
                    Upgrade JDK first     Any hard blockers?
                    (separate project)  (Undertow, EclipseLink,
                                         native images, internal
                                         starters, Derby)
                                                |
                                      +---------+---------+
                                      |                   |
                                     No                  Yes
                                      |                   |
                              Run 3.x -> 4.0       Resolve blocker
                              automation           first, then rejoin
                                      |
                                      v
                            Hop 4.0 -> latest 4.1.x
                                      |
                                      v
                            Verify (see Definition of Done)
                                      |
                                      v
                                 Production

The Migration Cluster: Which Guide Covers What

This article is the decision layer. Each of these is the mechanics for one layer — line-by-line breaking changes, before/after code, and test strategy.

Layer Guide Read it for
Boot-level changes Spring Boot 3 to 4 Migration Guide Starter modularization, Jackson 3 default, virtual threads, testing changes
Framework internals Spring Framework 6 to 7 Migration Guide Jakarta annotation cutover, removed APIs, AOT/GraalVM, JSpecify null safety
Security Spring Security 5 to 6 to 7 Migration SecurityFilterChain, lambda DSL, PathPatternRequestMatcher, silent authorization changes
JSON Jackson 2 to Jackson 3 Migration Guide tools.jackson rename, JsonMapper builder, enableDefaultTyping() removal
Data Spring Data JPA 3 to 4 Migration Guide (3 parts) hibernate-processor rename, JSpecify @NullMarked enforcement, query engine rewrite

The Five Things That Turn a Two-Week Migration Into a Two-Month One

  1. Undertow. No Servlet 6.1 release exists, Framework 7 requires it, and the Undertow-specific WebSocket and WebFlux classes were removed. Container migration to Tomcat 11+ or Jetty 12.1+ comes first, as its own project.
  2. Java below 17. A hard floor. From 8 or 11 this is a separate multi-week modernization — don’t run both in one branch.
  3. Internal starters. Auto-configuration still declared via spring.factories has been inert since Boot 3.0 removed that loading path; Boot 4’s modular layout is usually just where teams finally notice. Every internal library needs its own audit and release.
  4. Native images. Framework 7 moved to GraalVM’s unified reachability-metadata format: resource hints changed from regex to glob semantics (a single * no longer crosses a path segment), and a reflection hint on a type now implies its members. Every image needs rebuilding and re-verifying.
  5. Untested security behaviour. If you can’t currently prove which rule matches which endpoint, the Security 7 request-matcher and CSRF changes will move that behaviour silently. Building the test net is migration work, and it comes before the migration.

If any of these appear in your dependency inventory, stop estimating this as a two-week upgrade.

What NOT to Migrate at the Same Time

The most common way this project overruns isn’t technical difficulty. It’s scope. Avoid combining Boot 3 → 4 with a JDK jump, a database upgrade, a Hibernate rewrite, a Kubernetes migration, an observability redesign, and an architecture refactor. Every one of those is defensible on its own; together they make failure undiagnosable, because you can’t attribute a regression to a cause.

Change the framework first. Modernize the runtime second, unless the runtime is itself a blocker. That’s why the example POM below stays on Java 17 — Java 21 is a reasonable follow-up, deliberately excluded here.

A Two-Week Plan — For One Small, Well-Tested Service

What this estimate assumes

Two weeks is a worked example, not a planning figure to lift into a roadmap. It assumes a single small-to-medium service; a test suite you already trust, integration tests included; none of the five blockers listed above; a team with genuine focus; and no coupled modernization in the same branch. Change any one of those and the estimate moves, sometimes by weeks. Take the shape and the sequencing from this; derive the numbers from your own inventory.

The plan below is the sequence, not the calendar.

Week 1 — assessment and the 4.0 waypoint. Days 1-2: inventory exact Boot, Framework, Security and Jackson versions, and screen for the five blockers above. Run the migration recipe in dry-run mode to size the diff before committing to a date. Days 3-5: apply the composite recipe, then handle what it can’t — the javax.annotation/javax.inject sweep (these fail silently), ListenableFuture to CompletableFuture, and internal starters. Days 6-7: the security pass — convert any surviving chained .and() config, then build the authorization test net from the Security guide and check the CSRF-on-APIs default and PathPatternRequestMatcher trailing-slash change specifically.

Week 2 — verification, the 4.1 hop, cutover. Days 8-9: full integration suite, watching for JSpecify @NullMarked enforcement (a null into a non-nullable finder now throws rather than returning empty) and diffing serialized DTO samples against pre-migration output. Days 10-11: hop to the latest 4.1.x and sweep for the removed 4.0 deprecations. Days 12-13: staging soak with load. Day 14: cutover.

Migration Isn’t Done When It Compiles

A definition of done worth pasting into the ticket:

  • Application compiles on the latest Boot 4.1.x
  • Unit tests pass
  • Integration tests pass
  • Security authorization matrix passes (anonymous / wrong role / correct role, per route family)
  • JSON serialization compatibility diffed against pre-migration output
  • Database migrations verified
  • Production-like load test passes
  • Native image rebuilt and verified, if applicable
  • Observability verified (see below)
  • Dependency vulnerability scan passes
  • Container image rebuilt
  • Rollback artifact built and tested
  • Runbook updated
  • SBOM regenerated
  • Staging deployment successful
  • Production rollback procedure validated

Observability Is Migration Work, Not a Follow-Up

Boot 4 reorganized the observability modules and 4.1 continued the OpenTelemetry work. That means metric names, trace propagation, log structure, and actuator payload shapes can all move without a single test failing. Before cutover, verify: Micrometer metric names and tags still match what your dashboards query; traces still propagate across service boundaries and through @Async boundaries; log structure and correlation IDs are unchanged; actuator endpoints return the shapes your probes and scrapers expect; and your alerts still fire on the conditions they were written for.

A migration that passes tests but silently changes metric names, trace propagation, log structure, or actuator behaviour is not production-safe.

OpenRewrite: What It Will and Won’t Do

What the recipe catalog provides

The OpenRewrite Community Edition catalog provides the composite migration path to Boot 4.0 (org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0), plus individual 4.1 recipes such as SpringBootProperties_4_1. It does not currently provide an equivalent single composite 3.x → 4.1 recipe; the more extensive Moderne catalog is separately licensed. Community Edition recipes are published under the Moderne Source Available License, and the recipe documentation notes that non-commercial users can build the artifact from source. Artifacts are served from an authenticated repository rather than plain Maven Central — check that page for the current repository block and plugin versions before wiring this into CI.

Migration task Automation
Dependency version changes 🟢 High
Starter renames 🟢 High
Property migrations 🟢 High
Package and API transformations 🟡 Partial
Security authorization semantics 🔴 Manual
JSON output compatibility 🔴 Manual
Database behaviour 🔴 Manual
Nullability assumptions 🔴 Manual
Observability continuity 🔴 Manual
Performance regressions 🔴 Manual
Production verification 🔴 Manual

OpenRewrite can change your code. It cannot tell you whether your application still means the same thing.

Two recipe files, run as two passes

Keep these as separate files so it’s unambiguous that they’re two runs, not one:

rewrite-boot40.yml — pass 1:

type: specs.openrewrite.org/v1beta/recipe
name: com.ankurm.migration.LandOnBoot40
displayName: "Step 1 - Land Spring Boot 3.5.x on Spring Boot 4.0.x"
description: >
  Runs the Community Edition composite Boot 4.0 recipe: bumps the parent/BOM,
  renames modular starters (spring-boot-starter-web -> -webmvc, oauth2-client
  -> security-oauth2-client, and so on), migrates to Spring Framework 7.0 and
  Spring Security 7.0, and reports what it could not change automatically.
recipeList:
  - org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0

rewrite-boot41.yml — pass 2, run only after pass 1 compiles and tests are green:

type: specs.openrewrite.org/v1beta/recipe
name: com.ankurm.migration.LandOnBoot41
displayName: "Step 2 - Hop from 4.0.x to the latest 4.1.x"
description: >
  No single Community Edition composite recipe covers 4.0 -> 4.1, so this
  pairs the version bumps with the 4.1 properties migration. The removed-4.0-
  deprecation sweep (layertools jar mode, the old -DskipTests AOT behaviour,
  Apache Derby) remains a manual pass.
recipeList:
  - org.openrewrite.java.dependencies.UpgradeDependencyVersion:
      groupId: org.springframework.boot
      artifactId: spring-boot-dependencies
      newVersion: 4.1.1          # pin the concrete release you intend to ship
      overrideManagedVersion: true
  - org.openrewrite.maven.UpgradeParentVersion:
      groupId: org.springframework.boot
      artifactId: spring-boot-starter-parent
      newVersion: 4.1.1
  - org.openrewrite.java.spring.boot4.SpringBootProperties_4_1

On that newVersion value: OpenRewrite accepts a version pattern here — the published 4.0 recipe uses 4.0.x internally — but pinning a concrete release keeps the run reproducible and the diff reviewable. Substitute whichever 4.1.x is current when you run it.

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  --define rewrite.configLocation=rewrite-boot40.yml \
  --define rewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \
  --define rewrite.activeRecipes=com.ankurm.migration.LandOnBoot40 \
  --define rewrite.exportDatatables=true

Maven or Gradle

The examples here use Maven because it makes the dependency-management diff easier to show. The migration principles are identical for Gradle — adapt the inventory step (./gradlew dependencies rather than mvn dependency:tree) and the OpenRewrite invocation (the org.openrewrite.rewrite Gradle plugin, or the init-script form documented on the recipe page). Note that Boot 4.0 supports Gradle 9 alongside 8.14+, so a Gradle upgrade may land in the same window.

The POM diff

A representative service — web, OAuth2 client, JPA, an explicitly declared Jackson 2 JSR-310 module — before, on the last 3.5 patch:

<!-- pom.xml - Spring Boot 3.5.16, before -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.5.16</version>
</parent>

<properties>
  <java.version>17</java.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-jpamodelgen</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
  </dependency>
</dependencies>

After both passes, on the current 4.1.x — note the JDK deliberately stays at 17:

<!-- pom.xml - Spring Boot 4.1.1, after -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.1.1</version>
</parent>

<properties>
  <!-- Java 17 remains the minimum supported baseline for Boot 4.0 and 4.1.
       A JDK upgrade is a reasonable follow-up, deliberately kept out of
       this migration so regressions stay attributable. -->
  <java.version>17</java.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security-oauth2-client</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <!-- The Jackson 2 jackson-datatype-jsr310 dependency is dropped: Boot 4's
       managed Jackson 3 dependency set provides Java Time support through the
       new Jackson 3 module arrangement, and this Jackson 2 artifact could not
       be registered on a Jackson 3 mapper regardless - it lives in the
       com.fasterxml namespace, not tools.jackson. -->
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <annotationProcessorPaths>
          <path>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-processor</artifactId>
            <!-- was hibernate-jpamodelgen; version managed by the Boot BOM -->
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

Three changes there are easy to skim past. The starter renames are silent at compile time if you relied on the old artifact transitively rather than declaring it. The dropped Jackson 2 JSR-310 dependency fails loudly if any code still calls registerModule(new JavaTimeModule()) against a Jackson 3 mapper, since that class doesn’t exist in the tools.jackson namespace — details in the Jackson 3 guide. And hibernate-processor replacing hibernate-jpamodelgen is a build break rather than a runtime one: the metamodel classes stop generating, cascading into “cannot find symbol” wherever Criteria code references them.

A Prompt to Scope Your Fleet

You are auditing a Java/Spring Boot fleet for end-of-life exposure.
For each service, using its pom.xml or build.gradle:

1. Identify the exact Spring Boot version and, from that, the Spring
   Framework/Security/Jackson/Hibernate versions it manages.
2. Classify its OSS support status. Windows: 3.0 ended Dec 2023,
   3.1 Jun 2024, 3.2 Dec 2024, 3.3 Jun 2025, 3.4 Dec 2025,
   3.5 Jun 2026. 4.0 ends Dec 2026. 4.1 ends Jul 2027.
3. Flag the five high-cost blockers: Undertow as the servlet container,
   a Java baseline below 17, internal starters using spring.factories
   for auto-configuration, GraalVM native-image builds, and the absence
   of automated authorization tests. Also note EclipseLink, Apache Derby,
   and Kotlin codebases.
4. Order the services for triage. This is a prioritization heuristic for
   sequencing work, NOT a quantitative risk score - do not present it as
   one. Sort by, in order of weight:
     a. Is the service internet-facing or reachable by untrusted input?
     b. Does it handle sensitive, regulated, or authentication data?
     c. How long has its branch been without OSS patches?
     d. Does it use components with a recent unpatched advisory on its
        line (check spring.io/security against its exact versions)?
   Where two services tie, break the tie toward the one whose blast
   radius is larger, not the one whose version number is older.
5. Output a migration order, highest risk first, each service tagged with
   the blockers that apply and therefore whether a standard two-week plan
   is realistic for it.

Frequently Asked Questions

Is Spring Boot 3.5 really unsupported right now?

In open source, yes, since June 30, 2026. 3.5.16 was the final OSS patch for the 3.x line; there is no 3.6 and won’t be one. Commercial support for 3.5 remains separately available through June 2032 — a different question from whether your Maven Central builds receive patches.

Can I skip 4.0 and go straight from 3.x to 4.1?

Not as a single automated step. The Community Edition composite migration targets 4.0; it offers individual 4.1 recipes but no equivalent composite 3.x → 4.1 path. You pass through 4.0 as a waypoint — you just don’t deploy from there.

Do I need Java 21 for this migration?

No. Java 17 remains the minimum supported baseline for both 4.0 and 4.1. Framework 7’s Jakarta EE 11 / Servlet 6.1 baseline is the harder constraint — that’s what requires Tomcat 11 or Jetty 12.1 and rules out Undertow, independent of your JDK.

What if more CVEs land before I finish?

They will. The June 8 batch carried four Framework advisories; the August 20 batch spanned roughly a dozen projects across the portfolio. That cadence is the argument for starting assessment now rather than waiting for a quiet moment, because the quiet moment is not coming. If you can’t finish before the next disclosure, a paid support bridge buys the time to migrate properly; it doesn’t replace migrating.

The Real Migration Target Isn’t a Version Number

Spring Boot 3.x is no longer a safe default for teams relying on OSS support. But the decision in August 2026 isn’t simply “3 or 4.” It’s this: do we spend our engineering time migrating once, to a supported runway, or twice, because we stopped at the first milestone the automation happened to reach?

For most teams starting now, the answer is straightforward. Use Boot 4.0 as the migration waypoint. Deploy on the latest Boot 4.1.x. Automate the mechanical changes and manually verify the semantic ones — treating Security, Jackson, Data, serialization, and observability as migration work rather than as compiler errors that happen to be absent.

And don’t wait for your EOL branch to produce a vulnerability you can see. The absence of a patch is itself the problem.

See Also

Sources

Primary sources

Secondary sources

No Comments yet!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.