Upgrading Spring Boot: 2.x to 3.x to 4.x
Upgrades are a normal part of a Spring Boot project because each minor version has limited support. Success comes from upgrading in small steps, with tests, using the official tools. This page is a practical checklist for the two big jumps.
Strategy
Boot 2.7.x ──► latest 3.x (3.5.x) ──► 4.x
Step 1: upgrade to the LAST 2.7.x, remove all deprecation warnings
Step 2: move to Java 17+, then to Boot 3.0 ... 3.5 (fix jakarta.* and library versions)
Step 3: on the last 3.5.x remove deprecations that Boot 4 removes
Step 4: switch to Boot 4.x, one module at a time if needed (classic starters help)
Each step: green tests, deploy, observe. Never mix a framework upgrade with feature work.Tools
# 1. Automated code migration with OpenRewrite (free recipes)
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5
# 2. Detect renamed or removed properties while running
# add temporarily:
# <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-properties-migrator</artifactId><scope>runtime</scope></dependency>Use the official migration guides on the Spring Boot GitHub wiki for each version (Spring-Boot-3.0-Migration-Guide and the 4.0 guide), and read the release notes of every minor version you cross.
Boot 2.x to 3.x checklist
| Area | Change | Action |
|---|---|---|
| Java | Java 17 is the minimum | Upgrade the JDK and CI images |
| Jakarta EE | javax.* packages became jakarta.* (Servlet, JPA, Validation) | Replace imports (OpenRewrite does it). Upgrade libraries that use javax |
| Spring Security 6 | WebSecurityConfigurerAdapter removed, authorizeRequests replaced | Use SecurityFilterChain beans and authorizeHttpRequests with the lambda DSL |
| Auto-configuration registration | spring.factories replaced by AutoConfiguration.imports | Move entries for your own auto-configurations |
| Hibernate 6 | Different SQL generation, id generation defaults, removed @Type usages, dialect changes | Run all repository tests against the real database |
| Trailing slash matching | /users/ no longer matches /users | Fix clients or add explicit mappings |
| Actuator and metrics | Micrometer changes, Prometheus endpoint details | Recheck dashboards |
| Properties | Several renamed (e.g. spring.redis.* to spring.data.redis.*) | Use the properties migrator |
| HttpClient and RestTemplate | Apache HttpClient 5 replaces 4 | Update dependencies |
| Logging | Default log pattern changed slightly | Check parsers and alerts |
Boot 3.x to 4.x checklist
| Area | Change | Action |
|---|---|---|
| Baseline | Spring Framework 7, Jakarta EE 11, Java 17 minimum (21+ recommended) | Check servlet container version (Tomcat 11) |
| Modular code base | Auto-configuration split into smaller modules, starters renamed (for example spring-boot-starter-web to spring-boot-starter-webmvc) | Generate a fresh pom from start.spring.io and compare. Classic starters can ease the move |
| Jackson 3 | Default JSON library. Packages tools.jackson.*, unchecked exceptions, @JsonComponent replaced by @JacksonComponent | Update custom serializers and modules. Jackson 2 support can be kept temporarily |
| Testing | @MockBean and @SpyBean removed in favor of @MockitoBean and @MockitoSpyBean. Test slice annotations moved to modular packages | Update imports and dependencies (modular test starters) |
| Null-safety | JSpecify annotations across the portfolio | Optional: better IDE and Kotlin checks |
| Removed deprecated APIs | Things deprecated in 3.x are removed | Fix all deprecation warnings on 3.5 first |
| Third-party libraries | springdoc, Resilience4j, Spring Cloud, Lombok need Boot 4 compatible versions | Check compatibility tables before upgrading |
| New features | API versioning, resilience annotations (@Retryable, @ConcurrencyLimit), HTTP service clients improvements | Adopt after the upgrade is stable |
Typical code changes
// Boot 2 -> 3: imports
// before: import javax.persistence.Entity; javax.validation.Valid; javax.servlet.http.HttpServletRequest;
// after : import jakarta.persistence.Entity; jakarta.validation.Valid; jakarta.servlet.http.HttpServletRequest;
// Spring Security 5 style (removed)
// @Configuration public class Sec extends WebSecurityConfigurerAdapter {
// protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated(); } }
// Spring Security 6 style
@Configuration
@EnableWebSecurity
public class Sec {
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.anyRequest().authenticated());
return http.build();
}
}
// Boot 3 -> 4 tests
// before: @MockBean OrderService service;
// after : @MockitoBean OrderService service; (org.springframework.test.context.bean.override.mockito.MockitoBean)Rollout plan
- Create a dedicated branch and freeze feature work for the upgrade.
- Have strong tests first. Add integration tests with Testcontainers for database, messaging and security flows.
- Upgrade the build, compile, fix errors, run tests, then run the app locally and click through critical flows.
- Deploy to a test environment and compare metrics (startup time, memory, latency, error rate) before and after.
- Release with a canary or blue-green deployment so you can roll back quickly.
Common mistake: Jumping straight from Boot 2.5 to 4.0 in one commit. Too many changes at once make failures impossible to locate. Go through the latest release of each major line, and keep every step deployable.
Interview questions
Why can't I just change the version number?
Because packages, defaults and libraries changed (javax to jakarta, Hibernate 6, Security 6, Jackson 3). Code and configuration must be migrated, and third-party libraries must support the new baseline.
How long is a Spring Boot version supported?
Open source support for a minor version is about 12 months, with commercial support longer. Plan regular upgrades every 6 to 12 months to avoid a painful big jump.
What is the safest way to find breaking changes?
Upgrade in small steps, run the properties migrator, use OpenRewrite recipes, read the migration guide, and rely on a good automated test suite.