Skip to main content

Spring Boot 3 to Spring Boot 4 Guided Migration

Spring Boot 4 is not as disruptive as the Boot 2 to Boot 3 migration, but it is still a major upgrade. The safest path is:

current Boot 3.x -> latest Boot 3.5.x -> Boot 4.x

Spring's Boot 4.0 Migration Guide recommends upgrading to the latest available 3.5.x first, reviewing deprecations, checking dependency compatibility, and then moving to the latest Boot 4 maintenance release.

Migration Goal

By the end of this guide, the application should:

  • run on Spring Boot 4.x
  • use Spring Framework 7.x compatible dependencies
  • compile without removed Boot 3 APIs
  • use the new or renamed starters intentionally
  • pass unit, slice, and integration tests
  • expose the same operational endpoints that production depends on

Phase 0: Decide If The App Is Ready

Do this before touching the version number.

CheckWhy it matters
Already on latest Boot 3.5.xReduces the number of changes in the Boot 4 jump.
No Boot 3 deprecation warnings ignoredDeprecated Boot 3 APIs and properties may be removed in Boot 4.
Java 17+ in CI and productionBoot 4 requires Java 17 or later. Java 21 or newer is a better target for long-lived services.
Third-party libraries support Spring Framework 7 / Jakarta EE 11Your application can fail because of ecosystem dependencies, not only Spring Boot itself.
No reliance on Boot 4 removed integrationsUndertow, reactive Pulsar, Spring Session Hazelcast/MongoDB, and Boot Spock support are gone. See Removed in Boot 4.
Null-safety tooling reviewedBoot 4 uses JSpecify annotations; Kotlin and static analysis setups may surface new errors.
Test suite covers startup, web, security, persistence, and observabilityMajor upgrades punish weak integration tests.

Phase 1: Upgrade To Latest Boot 3.5 First

Upgrade your current application to the latest Spring Boot 3.5 line before moving to 4.x.

For Maven:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.x</version>
</parent>

For Gradle:

plugins {
id 'org.springframework.boot' version '3.5.x'
}

Then:

  1. Run the full test suite.
  2. Fix warnings from deprecated Spring Boot APIs, properties, and annotations.
  3. Check the dependency tree for libraries that pin old Spring, Jakarta, Jackson, Hibernate, Tomcat, Jetty, or testing versions.
  4. Commit this as a separate migration baseline.

Phase 2: Review The Platform Baseline

AreaBoot 3.5Boot 4
Java minimumJava 17Java 17
Recommended JavaJava 21Java 21 or newer
Spring Framework6.27.x
Jakarta EEJakarta EE 10 generationJakarta EE 11
Servlet APIServlet 6.0Servlet 6.1
KotlinEarlier Kotlin 2.xKotlin 2.2+
GraalVM native imageGraalVM 22.3+GraalVM 25+
GradleGradle 7.6+/8.xGradle 8.14+ or Gradle 9

Important point: Java 17 is still supported. You do not have to do a Java migration just to start Boot 4, but Java 21 is usually the healthier production target. See Spring Boot 4 system requirements.

Phase 3: Change The Boot Version

For Maven:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.x</version>
</parent>

For Gradle:

plugins {
id 'org.springframework.boot' version '4.0.x'
}

Now run:

./mvnw clean test
# or
./gradlew clean test

Expect failures. The first target is not "all green"; the first target is to classify failures into dependency, source-code, property, test, and runtime categories.

Phase 4: Replace Renamed Starters

Spring Boot 4 is more modular. Some older starter names still exist temporarily, but are deprecated. Prefer the new explicit names.

The naming pattern is now more consistent:

spring-boot-<technology>
spring-boot-starter-<technology>
spring-boot-<technology>-test
spring-boot-starter-<technology>-test
Boot 3 starterBoot 4 starter
spring-boot-starter-webspring-boot-starter-webmvc
spring-boot-starter-web-servicesspring-boot-starter-webservices
spring-boot-starter-aopspring-boot-starter-aspectj
spring-boot-starter-oauth2-clientspring-boot-starter-security-oauth2-client

HTTP clients are also more explicit:

Use caseStarter
RestClient / RestTemplatespring-boot-starter-restclient
Reactive WebClientspring-boot-starter-webclient

Common migration mistake: the old application relied on transitive dependencies. After modularization, add the starter for the technology you actually use.

Frequent misses after modularization:

spring-boot-starter-flyway
spring-boot-starter-liquibase
spring-boot-starter-security-test
spring-boot-starter-data-jpa-test

If a slice test or migration tool worked before only because another starter pulled it in transitively, add the matching technology starter explicitly.

Phase 5: Handle Jackson 3

Spring Boot 4 uses Jackson 3 by default. Many imports move from:

com.fasterxml.jackson...

to:

tools.jackson...

Jackson annotations remain under:

com.fasterxml.jackson.annotation

Review:

  • custom serializers and deserializers
  • custom ObjectMapper beans
  • JSON test helpers
  • libraries that still require Jackson 2
  • configuration properties under spring.jackson.*

Boot 4 auto-configures format-specific mapper types:

JsonMapper
XmlMapper

Defining a generic ObjectMapper bean may no longer replace Boot's JSON mapper. Prefer a JsonMapper bean when you need to customize JSON serialization.

Some properties become more specific:

# Boot 3
spring.jackson.read...
spring.jackson.write...

# Boot 4
spring.jackson.json.read...
spring.jackson.json.write...

If a dependency is not ready for Jackson 3, Boot 4 provides a deprecated Jackson 2 compatibility module. Treat it as a temporary bridge, not the final state:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-jackson2</artifactId>
</dependency>

See Introducing Jackson 3 support in Spring and the Jackson section of the Boot 4 migration guide.

Phase 6: Update Tests

Replace Boot Mockito Annotations

Boot-specific annotations are removed:

@MockBean
@SpyBean

Use Spring Framework annotations:

@MockitoBean
@MockitoSpyBean

Example:

@SpringBootTest
class OrderServiceTest {

@MockitoBean
private PaymentService paymentService;
}

Do not blindly search and replace. The new annotations are not valid in every old location, especially inside some test configuration patterns.

Review JSpecify Nullability

Boot 4 and the wider Spring portfolio use JSpecify nullability annotations. Annotations such as org.springframework.lang.Nullable should generally move to org.jspecify.annotations.Nullable. Code that compiled under Boot 3 may expose new nullable or non-nullable compilation errors under Boot 4, especially in Kotlin projects and codebases using static null analysis.

Add Explicit Test Auto-Configuration

@SpringBootTest is less magical in Boot 4. Add the test client you need explicitly.

For MockMvc:

@SpringBootTest
@AutoConfigureMockMvc
class ControllerTest {
}

For HTTP-server style tests:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class ApiIntegrationTest {
}

Boot 4 introduces RestTestClient, which can test both MockMvc-backed applications and real running HTTP servers.

Phase 7: Review Web And Server Changes

Boot 4 moves to Servlet 6.1 and newer embedded servers:

  • Tomcat 11
  • Jetty 12.1
  • Servlet 6.1

Undertow support has been removed. If your app uses Undertow, plan that as a separate migration decision rather than a quick dependency swap.

For WAR deployments to an external Tomcat server, use the runtime Tomcat starter:

<artifactId>spring-boot-starter-tomcat-runtime</artifactId>

Removed in Boot 4

Confirm the application does not depend on these before upgrading:

  • Undertow as an embedded server
  • Reactive Spring Pulsar support
  • Embedded Unix launch scripts for fully executable JARs
  • Spring Session Hazelcast
  • Spring Session MongoDB
  • Boot's Spock integration
  • @MockBean and @SpyBean
  • Deprecated Boot 3 APIs, classes, and properties

Review Spring Security 7

Boot 4 ships with Spring Security 7. For many applications, Security breaks before Boot-specific code does. If you can, prepare on Spring Security 6.5 while still on Boot 3.5 using the Preparing for 7.0 guide, then finish with the Migrating to 7.0 steps after the Boot 4 bump.

Also confirm the renamed OAuth2 client starter from Phase 4: spring-boot-starter-security-oauth2-client.

Watch for these Security 7 changes

1. Lambda DSL is required for HttpSecurity

The old chained style with .and() is no longer valid. Every HttpSecurity and ServerHttpSecurity configuration must use the lambda DSL:

http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/login")
.permitAll()
);

If login or authorization rules behave differently after the upgrade, this is often the first place to look. See Configuration migrations.

2. PathPatternRequestMatcher replaces Ant and MVC matchers

AntPathRequestMatcher and MvcRequestMatcher are no longer supported. Security 7 expects PathPatternRequestMatcher for requestMatchers(...) and related filter URLs.

Watch for:

  • custom setFilterProcessingUrl(...) calls on authentication filters
  • SwitchUserFilter exit/switch URLs
  • apps deployed behind a non-default servlet path prefix
  • JSP taglibs or custom WebInvocationPrivilegeEvaluator usage

If /api/** suddenly does not match the path you expect, review Web migrations.

3. Method security needs parameter names at runtime

If you use expressions like @PreAuthorize("@authz.check(#id)"), compile with -parameters so Spring can resolve method parameter names. Spring Framework 7 removed LocalVariableTableParameterNameDiscoverer, so missing parameter metadata now fails at runtime instead of silently mis-resolving. See Authorization changes.

4. OAuth2 resource server JWT validation changed

If you customize NimbusJwtDecoder, typ header validation moves from Nimbus internals to JwtTypeValidator. Custom jwtProcessorCustomizer type checks should be migrated before Boot 4 if you rely on non-default JWT header behavior. See OAuth 2.0 changes.

5. Security serialization moves to Jackson 3

Security's Jackson integration changes from SecurityJackson2Modules / ObjectMapper to SecurityJacksonModules / JsonMapper.Builder. This overlaps with Phase 5, but auth-heavy apps should test login, remember-me, OAuth2 login, and any persisted security context serialization explicitly.

If you use Spring Authorization Server, it now defaults to Jackson 3 as well.

Security-focused tests to run

  • form login and logout flows
  • CSRF-protected POST endpoints
  • OAuth2 login and resource server endpoints
  • method-level @PreAuthorize / @PostAuthorize rules
  • session or remember-me persistence across restarts

Phase 8: Review Data And Persistence

Boot 4 upgrades major data libraries, including:

  • Hibernate ORM 7.1
  • Jakarta Persistence 3.2
  • Hibernate Validator 9
  • HikariCP 7
  • Flyway 11
  • Liquibase 5
  • Testcontainers 2
  • Kafka 4.1
  • MongoDB driver 5.6

Run targeted tests for:

  • repository query methods
  • custom JPQL and native SQL
  • entity mappings
  • validation annotations
  • database migrations
  • transaction boundaries
  • Testcontainers setup

For Spring Batch, spring-boot-starter-batch uses an in-memory job repository by default. If you need database-backed job metadata, use:

<artifactId>spring-boot-starter-batch-jdbc</artifactId>

Also review ecosystem migration notes, not only Boot itself:

Elasticsearch

The old low-level Elasticsearch RestClient auto-configuration is replaced by Rest5Client. Customizers also change:

RestClientBuilderCustomizer // Boot 3
Rest5ClientBuilderCustomizer // Boot 4

MongoDB

Many properties move from spring.data.mongodb.* to spring.mongodb.*:

spring.mongodb.uri=
spring.mongodb.database=
spring.mongodb.username=
spring.mongodb.password=

UUID and BigDecimal representations may also need explicit configuration after the driver upgrade.

Redis

Redis observability is based more directly on Spring's Observation API, producing both metrics and spans. Validate dashboards and tracing after the upgrade if Redis is on the critical path.

Phase 9: Update Configuration Properties

Add the Spring Boot properties migrator temporarily:

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

Start the app and read the migration diagnostics. Fix renamed or removed properties, then remove the migrator.

Examples to check:

# Boot 3
spring.data.mongodb.*

# Boot 4
spring.mongodb.*
# Boot 3
management.tracing.enabled

# Boot 4
management.tracing.export.enabled

Actuator liveness and readiness probes are exposed by default:

/actuator/health/liveness
/actuator/health/readiness

If needed:

management.endpoint.health.probes.enabled=false

Other smaller but potentially surprising changes:

  • Logback output defaults more consistently to UTF-8.
  • DevTools LiveReload is disabled by default.
  • Maven optional dependencies are no longer placed inside executable uber JARs by default.
  • JDK-based HTTP clients use virtual threads when spring.threads.virtual.enabled=true.
  • Spring Retry is no longer version-managed by Spring Boot; prefer Spring Framework retry support where possible.

Phase 10: Use New Capabilities Intentionally

Do not mix feature adoption with the core migration unless the feature directly simplifies the upgrade.

Consider these after the app is stable:

FeatureWhy it mattersOfficial reference
HTTP Service ClientsSpring can generate clients for @HttpExchange interfaces.HTTP Service Client enhancements
Built-in API versioningMVC and WebFlux gain API versioning auto-configuration.API versioning in Spring
OpenTelemetry starterBoot 4 can auto-configure the OpenTelemetry SDK and OTLP export.Boot 4 migration guide
JSpecify null safetyBetter static analysis, especially for Kotlin and null-sensitive codebases.Null-safe applications with Boot 4

API versioning can be configured with properties such as:

spring.mvc.apiversion...
spring.webflux.apiversion...

Example HTTP Service Client:

@HttpExchange("/users")
public interface UserClient {

@GetExchange("/{id}")
User getUser(@PathVariable Long id);
}

Pull Request Checklist

Use this as the migration PR review checklist.

  • Application first upgraded cleanly to latest Boot 3.5.x
  • Deprecated Boot 3 APIs and properties removed
  • Java, Kotlin, Gradle, and GraalVM baselines reviewed
  • New Boot 4 starter names used where applicable, including Flyway, Liquibase, and test starters
  • Jackson 3 imports, JsonMapper customizations, and spring-boot-jackson2 bridge usage reviewed
  • @MockBean and @SpyBean replaced intentionally
  • @SpringBootTest tests have explicit client auto-configuration where needed
  • JSpecify/nullability changes reviewed for Kotlin and static analysis tooling
  • Spring Security 7 migration notes reviewed, including lambda DSL, path matchers, method security, OAuth2 JWT validation, and Security Jackson 3
  • Spring Data 2025.1 release notes reviewed
  • Undertow and other removed integrations confirmed absent
  • Data-library upgrade risks tested, including Elasticsearch, MongoDB, and Redis if used
  • Batch job repository behavior checked
  • Actuator, tracing, metrics, and health probes validated
  • Properties migrator run and then removed
  • Production smoke test completed

Common Failure Map

SymptomLikely causeFirst fix
Missing web classesStarter modularizationAdd spring-boot-starter-webmvc, restclient, or webclient explicitly.
Flyway or Liquibase no longer runsTransitive starter removedAdd spring-boot-starter-flyway or spring-boot-starter-liquibase explicitly.
Slice test context fails to startMissing technology test starterAdd the matching spring-boot-starter-*-test dependency.
Jackson imports failJackson 3 package moveUpdate imports to tools.jackson.* where required.
Custom JSON config ignoredObjectMapper bean no longer overrides Boot defaultsDefine a JsonMapper bean instead.
Mock-based tests fail@MockBean removedMove to @MockitoBean and adjust placement.
MockMvc not injected@SpringBootTest no longer provides it automaticallyAdd @AutoConfigureMockMvc.
New Kotlin/null-analysis errorsJSpecify migrationReplace Spring nullability annotations with org.jspecify.annotations.*.
Security config fails to compileLambda DSL now requiredRewrite HttpSecurity config to lambda style; remove .and() chaining.
Authorization rules no longer matchPathPatternRequestMatcher migrationReplace Ant/MVC matchers and review servlet path prefixes.
@PreAuthorize fails on #param namesParameter names not available at runtimeCompile with -parameters and retest method security.
JWT resource server rejects valid tokenstyp header validation movedMigrate custom JWT type checks to JwtTypeValidator.
Login or OAuth2 flow breaks after Jackson upgradeSecurity Jackson 3 migrationMove from SecurityJackson2Modules to SecurityJacksonModules and test auth flows.
Security config breaks at runtimeOther Spring Security 7 changesReview the Security 7 migration guide.
Repository or query behavior changedSpring Data 2025.1 / Hibernate 7Review Spring Data 2025.1 and Hibernate 7 migration notes.
Batch metadata no longer written to DBBatch starter default changedUse spring-boot-starter-batch-jdbc.
Elasticsearch client wiring failsRestClient replaced by Rest5ClientUpdate customizers to Rest5ClientBuilderCustomizer.
MongoDB config ignoredProperty prefix changedMove settings from spring.data.mongodb.* to spring.mongodb.*.
Undertow dependency failsUndertow support removedMove to Tomcat or Jetty.
Health endpoint output changedProbes exposed by defaultValidate or configure liveness/readiness groups.

Official References

Spring Boot

Spring portfolio

Data and infrastructure libraries

This guide is based on project experience and the official Spring migration notes. Treat it as a guided checklist, then verify critical production decisions against the official docs for the exact Boot 4.x version you adopt.