Spring Boot 4 Migration Guide Seamlessly Upgrade Your Java Applications | AZArticle
Instant approval is live — your article publishes the moment you submit. Read the guidelines

Spring Boot 4 Migration Guide Seamlessly Upgrade Your Java Applications

Author: Priya Sinha Published On: 29 Oct 2026 Computers and Technology 1 view

Spring Boot 4 represents a major evolutionary leap for modern Java microservices. Built on top of Spring Framework 7 and Jakarta EE 11, Spring Boot 4 introduces explicit modularity, native API versioning support, declarative HTTP clients, and runtime enhancements powered by Java 17+ baseline requirements.

However, jumping straight into a Spring Boot 4 upgrade without a structured blueprint can trigger build failures, silent security blocks, and broken dependencies. This Spring Boot 4 migration guide walks engineering teams through a zero-downtime, phase-based upgrade strategy.

Step 1: Baseline on Spring Boot 3.5 First

Before changing your major version dependency, upgrade to the latest patch of Spring Boot 3.5.x. Spring Boot 3.5 acts as an essential discovery bridge, surfacing deprecation warnings for methods that are completely removed in version 4.0. Clear all compilation warnings and attach the spring-boot-properties-migrator module to identify renamed properties at startup.

Step 2: Audit System Requirements & Server Baseline

Confirm your environment aligns with Spring Boot 4 infrastructure requirements:

Step 3: Address Key Breaking Changes

  1. Jackson 3 Integration: Convert legacy ObjectMapper configurations to JsonMapper or XmlMapper beans.

  2. Spring Security 7 Enforcement: Ensure deprecated adapters like WebSecurityConfigurerAdapter are removed. Configure explicit SecurityFilterChain bean definitions for state-changing requests.

  3. Modular Starters: Update dependency declarations to adopt focused starter POMs, or utilize spring-boot-starter-classic as a temporary migration bridge.

By systematically tackling deprecations on Spring Boot 3.5 and verifying third-party library compatibility, your team can unlock faster startup times, smaller container footprints, and modern cloud-native capabilities without operational friction.

Exploring Spring Boot 4's new features and their real-world impact

Spring Boot 4 touches nearly every layer of the framework from how jars are packaged, to how nullability is expressed, to how you call other services over HTTP. Below is a deep look at each major feature, what changed from Spring Boot 3.x, and what it looks like in code.

1. Codebase Modularization — Smaller, Focused Jars

In Spring Boot 3.x and earlier, autoconfiguration was largely monolithic: a handful of large starters (spring-boot-starter-web, spring-boot-starter-data-jpa, and so on) pulled in broad, all-in-one classpaths. Spring Boot 4 breaks this apart into smaller, more focused jars, so your application only carries the autoconfiguration classes it actually needs.

Why it matters:

2. JSpecify Null safety — A real standard, not vendor annotations

This is arguably the most consequential change for day-to-day code in Spring Boot 4. For a decade, Spring shipped its own @Nullable / @NonNull / @NonNullApi / @NonNullFields annotations in org.springframework.lang. They were useful as IDE hints but had no formal specification and couldn't express nullability on generics, arrays, or type parameters. As of Spring Framework 7 and Spring Boot 4, those annotations are deprecated in favor of JSpecify — a standard built jointly by Google, JetBrains, and Oracle that every major IDE and static analysis tool can understand natively.

Before (Spring Boot 3.x, package-level annotations):

java
@NonNullApi
@NonNullFields
package com.example.orders;

import org.springframework.lang.Nullable;

Now (Spring Boot 4, JSpecify):

java
@NullMarked
package com.example.orders;

import org.jspecify.annotations.NullMarked;

java
@NullMarked
public class OrderService {

   public Order createOrder(String email, @Nullable String promoCode) {
       // email is guaranteed non-null by the package's @NullMarked default
       // promoCode is explicitly allowed to be null
       ...
   }
}

@NullMarked flips the default for a package, class, or module to "non-null unless marked otherwise" — which matches how most people actually reason about their code. JSpecify also handles nullability on generics, something the old Spring annotations simply couldn't express:

java
// A non-null list that may contain null elements
public void processOptionalTags(List<@Nullable String> tags) { ... }

Migration note: @Nullable on a method's return type is now a type-use annotation, so it moves from above the method signature to directly in front of the return type — a small but easy-to-miss syntax shift when migrating existing code.

3. First-Class API Versioning

Before Spring Boot 4, API versioning had no official answer, teams rolled their own with URL prefixes (/v1/, /v2/), custom HandlerMapping subclasses, or interceptors, and every service ended up doing it slightly differently. Spring Framework 7 adds a version attribute directly to @RequestMapping and its shortcuts (@GetMapping, @PostMapping, etc.), with auto-configuration in Spring Boot 4 for both Spring MVC and WebFlux.

You choose a versioning strategy — URL path segment, request header, query parameter, or media type — with a single property:

properties
# Route by request header
spring.mvc.apiversion.use.header=API-Version
spring.mvc.apiversion.default=1.0

Then declare versions directly on your controller methods:
java
@RestController
@RequestMapping("/accounts")
class AccountController {

   @GetMapping(path = "/{id}", version = "1.0+")
   AccountV1 getAccountV1(@PathVariable long id) {
       return new AccountV1(id, "legacy-name");
   }

   @GetMapping(path = "/{id}", version = "2.0")
   AccountV2 getAccountV2(@PathVariable long id) {
       return new AccountV2(id, "name", "email");
   }
}

Because the routing strategy lives in configuration rather than in the controller, you can switch strategies without touching controller code—simplifying long-term maintenance in complex systems where APIs and microservices integration form the core architecture. You can also announce a deprecation via the Sunset header effortlessly without modifying a single controller method.

4. Declarative HTTP Service Clients

Spring Framework 6 introduced @HttpExchange interfaces as an alternative to hand-written RestTemplate or WebClient code, but wiring them up still required manually building an HttpServiceProxyFactory and registering a bean for every interface. Spring Boot 4 removes that ceremony with the new @ImportHttpServices annotation, which auto-registers your HTTP interfaces as proxy beans.

Before (Spring Boot 3.x, manual WebClient calls):

java
public User getUser(Long id) {
   return webClient.get()
       .uri("/users/{id}", id)
       .retrieve()
       .bodyToMono(User.class)
       .block();
}
Now (Spring Boot 4, declarative HTTP interface):

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

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

   @PostExchange
   User createUser(@RequestBody User user);

   @GetExchange
   List<User> fetchUsersByType(@RequestParam String type);
}

java
@Configuration
@ImportHttpServices(group = "userApi", types = UserClient.class)
class HttpClientConfig {
}
properties
spring.http.client.service.group.userApi.base-url=https://api.example.com
spring.http.client.service.read-timeout=2s

Spring generates the implementation at runtime, so a service layer just injects UserClient and calls client.fetchUser(id) like any other bean. Teams that previously reached for Spring Cloud OpenFeign for this kind of declarative client now get most of the same ergonomics natively, with tighter integration into Spring Security 7 and no extra dependency.

5. Jackson 3 — A bigger change

Spring Boot 4 moves from Jackson 2 to Jackson 3, and this is one of the changes most likely to bite teams that upgrade without reading the fine print. Instead of a single ObjectMapper bean, Spring Boot now auto-configures format-specific mappers: a JsonMapper for JSON and an XmlMapper for XML.

Before (Spring Boot 3.x):

java
@Bean
public ObjectMapper objectMapper() {
   return new ObjectMapper().registerModule(new JavaTimeModule());
}

Now (Spring Boot 4):

java
@Bean
public JsonMapper jsonMapper() {
   return JsonMapper.builder()
       .addModule(new JavaTimeModule())
       .build();
}
Defining an ObjectMapper bean alone is no longer sufficient to override the auto-configured mapper — you need a JsonMapper or XmlMapper bean instead. There's also a quieter, more dangerous change: Jackson 2's JsonProcessingException extended IOException, but Jackson 3's JacksonException extends RuntimeException. Any catch (IOException e) block that was silently catching Jackson parse errors will stop catching them after the upgrade — a bug that tends to surface in production rather than in a unit test.

For teams that need a faster interim fix, Spring Boot ships a spring.jackson.use-jackson2-defaults property and a spring-boot-jackson2 compatibility starter to ease the transition.

6. Observability, Security, and Messaging (Spring Boot 4.1)

Spring Boot 4.1, released in June 2026, builds on the 4.0 foundation with a smaller but production-focused set of additions:


P

Priya Sinha

Published on 29 Oct 2026  |  Category: Computers and Technology

Comments (0)


Back to Articles