Spring Boot 4.0: What's New and How to Migrate from 3.x

Spring Boot 4.0 is the most significant update since the jump to Spring Boot 3.0. It requires Spring Framework 7.0, Java 17 as the minimum (with full support for Java 21+), and introduces important breaking changes alongside new capabilities that simplify API development, observability, and testing. This guide covers the most relevant new features and how to migrate from 3.x.

Prerequisites

Spring Boot 4.0 raises the minimum versions across the entire ecosystem:

DependencyMinimum version
Java17 (21+ recommended)
Spring Framework7.0
Spring Security7.0
Jackson3.0
Hibernate7.1
Tomcat11.0
Gradle8.14+ / 9.x
// build.gradle.kts
plugins {
    id("org.springframework.boot") version "4.0.6"
    id("io.spring.dependency-management") version "1.1.7"
    kotlin("jvm") version "2.1.0"
    kotlin("plugin.spring") version "2.1.0"
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

HTTP Service Clients: REST Without Boilerplate

One of the most anticipated new features. Spring Boot 4.0 auto-configures implementations of interfaces annotated with @HttpExchange, eliminating the need to manually implement REST clients.

// Define the interface — Spring generates the implementation
@HttpExchange("https://api.github.com")
public interface GitHubClient {

    @GetExchange("/users/{username}")
    GitHubUser getUser(@PathVariable String username);

    @GetExchange("/repos/{owner}/{repo}")
    GitHubRepo getRepo(@PathVariable String owner, @PathVariable String repo);
}

// Register the client as a bean
@Configuration
public class ClientConfig {

    @Bean
    public GitHubClient gitHubClient(RestClient.Builder builder) {
        var restClient = builder.baseUrl("https://api.github.com").build();
        var factory = HttpServiceProxyFactory.builderFor(RestClientAdapter.create(restClient)).build();
        return factory.createClient(GitHubClient.class);
    }
}

// Inject and use directly
@Service
public class GitHubService {

    private final GitHubClient client;

    public GitHubUser getUserInfo(String username) {
        return client.getUser(username); // HTTP call managed automatically
    }
}

Built-in API Versioning for Spring MVC and WebFlux

Spring Boot 4.0 adds auto-configuration for API versioning without external libraries:

# application.yml
spring:
  mvc:
    apiversion:
      enabled: true
      default-version: "1"
      supported-versions: "1,2"
      header-name: "X-API-Version"          # by header
      # param-name: "api-version"            # or by query param
      # media-type-prefix: "application/vnd.myapp.v"  # or by media type
@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping
    @ApiVersion("1")
    public List<UserV1Dto> getUsersV1() {
        return userService.findAllV1();
    }

    @GetMapping
    @ApiVersion("2")
    public List<UserV2Dto> getUsersV2() {
        return userService.findAllV2(); // Enriched response in V2
    }
}

OpenTelemetry Starter: Observability Out of the Box

New dedicated starter that auto-configures the OpenTelemetry SDK and exports metrics and traces via OTLP:

// build.gradle.kts
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-opentelemetry")
}
# application.yml — just define the endpoint
management:
  opentelemetry:
    resource-attributes:
      service.name: "my-service"
      service.version: "4.0.6"
    otlp:
      endpoint: "http://otel-collector:4318"
      export:
        traces: true
        metrics: true
        logs: true

No more manual OpenTelemetry SDK configuration — Spring Boot handles it together with the Micrometer integration.

Virtual Threads with JDK HttpClient

Spring Boot 4.0 leverages Virtual Threads (Project Loom) in more places. The JDK HttpClient now uses them automatically when enabled:

spring:
  threads:
    virtual:
      enabled: true  # Enables Virtual Threads across the application
// This synchronous code now scales like reactive
@Service
public class ExternalApiService {

    private final GitHubClient gitHubClient;

    // Each call runs on a Virtual Thread — no thread pool blocking
    public List<GitHubUser> getTeamMembers(List<String> usernames) {
        return usernames.parallelStream()
            .map(gitHubClient::getUser)
            .toList();
    }
}

Important Breaking Changes

Jackson 3.0

Jackson 3.0 removes some deprecated APIs. Jackson 2 support exists but in deprecated form:

// build.gradle.kts — if you depend on Jackson 2 APIs, upgrade to Jackson 3
dependencies {
    // Jackson 3 is included with Spring Boot 4.0
    // If you have direct Jackson 2 dependencies, update them:
    implementation("com.fasterxml.jackson.core:jackson-databind:3.0.2")
}

Renamed Properties

Add spring-boot-properties-migrator temporarily to detect deprecated properties:

dependencies {
    runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")
}

Most common renames:

Old propertyNew property
management.tracing.enabledmanagement.tracing.export.enabled
spring.dao.exceptiontranslation.enabledspring.persistence.exceptiontranslation.enabled
Some server.error.* propertiesmoved to root level

Auto-Configuration: Public API Removed

Public members of auto-configuration classes have been removed. If you extended or referenced classes like DataSourceAutoConfiguration directly, you need to refactor:

// ❌ No longer compiles in Spring Boot 4.0
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
DataSourceAutoConfiguration.SomeMember member = ...;

// ✅ Use the configured beans directly
@Autowired
DataSource dataSource;

Improved Testing: @MockitoBean and RestTestClient

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {

    @MockitoBean  // Definitively replaces @MockBean
    private UserService userService;

    @Autowired
    private RestTestClient restTestClient; // New in Spring Boot 4.0

    @Test
    void shouldReturnUser() {
        when(userService.findById(1L)).thenReturn(mockUser());

        restTestClient.get()
            .uri("/api/users/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody(UserDto.class)
            .value(user -> assertThat(user.name()).isEqualTo("Test"));
    }
}

Console Logging Control

New property for environments where you only want file logging:

logging:
  console:
    enabled: false   # Disables console output — useful in production
  file:
    name: /var/log/app.log

Migration Checklist from 3.x

  1. Update Java to 17 minimum (21 recommended)
  2. Add spring-boot-properties-migrator and run the app — review the warnings
  3. Update Jackson to 3.x if you have direct dependencies
  4. Replace @MockBean with @MockitoBean across all tests
  5. Review auto-configurations you extend or that reference internal classes
  6. Migrate to OpenTelemetry Starter if you use Zipkin/Jaeger manually
  7. Test in staging with production-like traffic before migrating

Conclusion

Spring Boot 4.0 consolidates the modernization of the Spring ecosystem: less boilerplate for HTTP clients, built-in API versioning, OpenTelemetry observability from day one, and Virtual Threads in more application layers. Migration from 3.x requires attention to breaking changes, but the properties-migrator and deprecation messages guide the process clearly.