Article

Spring Boot 4 features and migration

Overview of the main new features of Spring Boot 4 and migration tips with OpenRewrite.

Spring Kotlin
Intermediate
Florian Beaufumé
Florian Beaufumé LinkedIn X GitHub
Published 24 Jan 2026 - Last updated 1 Mar 2026 - 7 min read
Spring Boot 4 features and migration

Table of contents

Introduction

Spring Boot 4 was released a couple of months ago. At the time of writing, we even have a 4.0.1 patch. So I think it is the right time to start migrating.

In this article, I will present some of the new features of Spring Boot 4 such as modularity, null safety, API versioning, resilience, HTTP service client, Jackson 3 support and others.

I will also explain how you can get a quick start on your migration with OpenRewrite.

The articles in this series:

  1. Spring Boot 4 features and migration (this article): baseline, modularity, null safety, API versioning, Jackson 3, resilience, RestTemplate, HTTP service client, OpenRewrite.
  2. Spring Boot 4 test features: modularity, application context pausing, JUnit 6, Mockito, RestTestClient.

Baseline

As expected, Spring Boot 4 has updated many baseline versions. Here is a summary of the main ones:

Component Spring Boot 3 Spring Boot 4
Java 17 17
Kotlin 1.9 2.2
Spring Framework 6 7
Jakarta EE 10 11
JPA 3.1 3.2
Hibernate 6 7
Tomcat 10 11
Jackson 2 3
JUnit 5 6

The Java baseline remains version 17. It is nevertheless recommended to use more recent LTS (long-term support) versions of Java such as 21 or 25. If you plan to use virtual threads you should jump to Java 25 to prevent thread pinning on synchronized.

Spring Framework upgraded from version 6 to 7 with many new features and improvements. The major ones are described in this article. The compatibility remains very high, but you may encounter some breaking changes in terms of dependencies, packages, classes or methods. The next sections will detail some of these changes and explain the reasons behind them.

Modularity

For a long time, a lot of Spring Boot magic came from one big jar named spring-boot-autoconfigure. It provided autoconfiguration for many Spring supported features, even the ones that a given business application does not need.

For Spring Boot 4, the Spring teams decided to split the autoconfiguration into multiple smaller jars. This can help reduce the application size and the startup time. As a consequence, you now have to be more explicit about the features you want to use.

For example, to use RestClient in a Spring MVC application with Spring Boot 3, the spring-boot-starter-web was enough. You now need to add the spring-boot-starter-restclient starter in our pom.xml:

<!-- Spring Boot 4 starter to expose web MVC endpoints -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- Spring Boot 4 starter to call web endpoints with RestClient -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient</artifactId>
</dependency>

I will give another example with Liquibase. With Spring Boot 3, we used to add the org.liquibase:liquibase-core dependency. Now we use a starter instead:

<!-- Spring Boot 4 starter for Liquibase -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-liquibase</artifactId>
</dependency>

From the previous RestClient dependencies extract, you can see that the web starter is now named spring-boot-starter-webmvc. It was renamed from ...-web to ...-webmvc to better distinguish it from the WebFlux starter.

To help migrate the dependencies of your existing application, you can use OpenRewrite as explained later.

You can alternatively use Spring Initializr. It is a great tool to generate new Spring Boot 4 projects. But it is also helpful to understand the dependency changes to perform when migrating Spring Boot 3 projects:

  • Open Spring Initializr
  • Make sure to select a Spring Boot 4+ version.
  • On the right side, search and add the dependencies you want to use, such as "Web", "RestClient", "Liquibase", etc.
  • To generate and download a new project template, click on "Generate".
  • To view the Spring Boot 4 compliant pom.xml, click on "Explore" and apply the dependencies to your migrating project.

In addition to dependency changes, Spring Boot 4 brings package changes. They chose to follow a common base package pattern: org.springframework.boot.<technology>[.test]. For example org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc becomes org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc. OpenRewrite can also help you migrate.

Null safety

Contrary to Kotlin, Java does not natively support null-restricted types. It means that you cannot distinguish between nullable and non-nullable types usages with the language only. There is a Java proposal to fill that gap, but it is not available yet.

Several initiatives emerged over time to address that limitation. They rely on annotations to declare the types nullability. But they had relative success so far.

JSpecify is another initiative. It comes from multiple organizations and aims to provide standard annotations for Java nullness analysis. As you have guessed, it has been adopted by Spring Boot 4. As a consequence it may have a better future.

With JSpecify, you can define the default nullness behavior of a package. For example, to declare all method parameters and return values as non-null by default, add a package-info.java file to your package with this content:

@NullMarked
package com.mycompany.myapplication;

import org.jspecify.annotations.NullMarked;

Then, you can override that default behavior for specific usages. For example, to declare the note parameter as nullable:

import org.jspecify.annotations.Nullable;

public class NotificationService {

public void sendNotification(String email, @Nullable String note) {
// ...
}
}

When supported by the IDE (this is the case for IntelliJ), a warning is displayed if you try to pass null to a non-null parameter such as email.

Using annotations and lacking operators (such as safe call or Elvis) feels verbose and restrictive compared to Kotlin. But it is still an improvement for Java.

The big change for Spring Boot 4, is the support of JSpecify by many Spring projects. They updated the codebase of Spring Boot, Framework, Data, Security and many other projects to fully declare the nullness of the various type usages. For some projects, such as Spring AI, the migration is currently still in progress.

This helps you write better Java code. If you use Kotlin, it also helps you by not relying anymore on platform types.

The JSpecify support by Spring Boot 4 is also an opportunity to use its annotations in your Java business code. If you choose to do so, you should also consider NullAway, a tool that can be used with Maven or Gradle to check nullness at compile time. It will give you stronger guarantees by making your build fail if a nullness violation is detected.

API versioning

Declarative API versioning is now supported in Spring MVC and Spring WebFlux. Instead of coding the versioning logic yourself, you can declare the version in the mapping annotations such as @GetMapping, @PostMapping, etc. Then you configure the versioning mode.

Four different versioning modes are supported:

Mode Example
Path /api/v2/hello
Query /hello?version=2
Header X-API-Version: 2
Media type Accept: application/json; version=2

For example in a basic REST controller, it is now possible to have multiple endpoints for the same path but different versions (the version attribute is new):

@RestController
public class HelloController {

@GetMapping(path = "/hello", version = "1")
String sayHelloV1() {
return "Hello from version 1";
}

@GetMapping(path = "/hello", version = "2+")
String sayHelloV2() {
return "Hello from version 2";
}
}

To configure the versioning mode, you can use properties or Java configuration. A properties based example:

spring.mvc.apiversion.use.header=X-API-Version
spring.mvc.apiversion.default=2

The first property enables the header mode and specifies the header name. The second one defines the default version when the header is not sent by the HTTP client.

Another example, using a Java configuration, for path based versioning:

@Configuration
public class ApiConfig implements WebMvcConfigurer {

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api/v{version}", forAnnotation(RestController.class));
}

@Override
public void configureApiVersioning(ApiVersionConfigurer configurer) {
configurer.usePathSegment(1);
}
}

The first method defines the version path format. The second one identifies the path segment that Spring will parse to extract the version.

Jackson 3

By default, Spring Boot 4 will use Jackson version 3. It is nevertheless possible to use version 2 if needed.

This new major version brings important changes. The Maven group ID changed from com.fasterxml.jackson to tools.jackson. The base package changed from com.fasterxml.jackson to tools.jackson for most classes except the annotations that remain in the original package.

Exceptions are now unchecked and extend tools.jackson.core.JacksonException.

Deprecated methods and classes have been removed.

Several default settings were changed. If this is a problem you can set spring.jackson.use-jackson2-defaults to true in your Spring Boot configuration.

ObjectMapper still exists and is now immutable. But you may prefer using the new JsonMapper class to convert to and from JSON. An instance is created and injected by Spring. You can customize it with various spring.jackson. properties or with a JsonMapperBuilderCustomizer in a @Configuration class:

@Bean
JsonMapperBuilderCustomizer jsonMapperBuilderCustomizer() {
return builder -> builder
...
.build();
}

You can also use specialized mappers such as XmlMapper or YamlMapper.

For additional information see Jackson 3 Migration Guide.

Resilience

The Spring Retry project has supported resilience features for a long time. With Spring Boot 4, some of these features, such as @Retryable and @ConcurrencyLimit are now natively supported in Spring Framework. No need to add a specific dependency, but do not forget to add @EnableResilientMethods to one of your configuration classes.

@Retryable can be used to automatically retry a failed operation.

// 2 retries, so will be executed at most 3 times
@Retryable(maxRetries = 2, delay = 500)
public void doSomething() {
....
}

You can configure the number of retries, the delay between retries, the exceptions that will or will not trigger a retry, etc.

@ConcurrencyLimit can be used to limit the number of concurrent calls to a method.

// Max 2 concurrent calls
@ConcurrencyLimit(2)
public void doSomething() {
....
}

Note that these annotations suffer from the same local call trap as Spring annotations such as @Transactional, @PreAuthorize and @Cacheable. It means that the annotations are not used when called from the same component.

RestTemplate deprecation

The days of good old RestTemplate are numbered. It is still available in the Spring Framework version from Spring Boot 4, but it is now soft deprecated. It means that it is described as deprecated in the documentation, but not yet marked as deprecated in the code. It will be and then removed at some point though.

If you still use RestTemplate, first upgrade to Spring Boot 4 then consider migrating to Spring alternatives such as:

  • RestClient, the modern equivalent for web MVC applications.
  • WebClient, the reactive counterpart, for example in WebFlux applications.
  • HTTP service client, a declarative approach, see next section.

HTTP service client

HTTP service client is a declarative approach, similar to Feign, to define HTTP clients in Spring applications. It was improved in Spring Framework 7. The boilerplate code is now reduced while keeping flexibility.

As before, you can declare the HTTP client as an interface with some annotations:

public interface BookClient {

@GetExchange("/books")
List<Book> getBooks();

@GetExchange("/books/{id}")
Book getBook(@PathVariable Long id);

@PostExchange("/books")
Book createBook(@RequestBody Book book);
}

The clients registration is now simplified. You can use the new @ImportHttpServices annotation in a @Configuration class:

@Configuration
@ImportHttpServices(group = "library", types = {BookClient.class, AuthorClient.class})
public class HttpServicesConfig {
}

The clients can be declared by type or by package. The group name is optional and used to share some configuration. For example, you can define the base URLs and some timeouts in your application.properties:

spring.http.clients.connect-timeout=2s
spring.http.serviceclient.library.base-url=https://api.acme.com
spring.http.serviceclient.library.read-timeout=5s

The connection timeout is set for all HTTP clients, while the read timeout is specific to the library group.

As expected, programmatic registration or configuration is also possible.

Finally, you can inject and use the HTTP service client instances in your components and use them in the same way as before.

Migration with OpenRewrite

OpenRewrite is an open source tool, written in Java, to execute automated file modifications such as code refactoring or frameworks migration. The file modification rules are called recipes.

OpenRewrite comes with many open source recipes, but Moderne (the company behind OpenRewrite) also provides paid recipes. As you have guessed, there are recipes to migrate to Spring Boot 4. In this section I will focus on the open source one, and I will migrate a Maven project.

Upgrading to Spring Boot 4 with OpenRewrite is done by executing a command line. But before executing the command line, some tips:

  • Make sure you migrate first to the latest Spring Boot 3.5.x release, using OpenRewrite or any other way.
  • Make sure that the project builds correctly and that the tests run fine.
  • Commit or stash your Git changes and open a dedicated branch for the migration.

OpenRewrite can be declared in the pom.xml file of your project. This is useful for refactoring recipes you want to execute regularly. But for one shot recipes such as framework migrations, the command line is more convenient.

Now you can execute this command line next to the main pom.xml file of your project:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 -Drewrite.exportDatatables=true

It will automatically pick the latest version of OpenRewrite and the Spring Boot 4 recipe.

OpenRewrite will analyze your project and modify the files (pom.xml, Java sources, etc). It may take a while such as minutes or tens of minutes for large projects on slow machines. It will apply many of the Spring or Jackson changes we mentioned before. The recipe may not cover all aspects of the migration, but it should help.

When the command line has finished:

  • Review the modifications. This is important to make sure everything is fine but also to learn about some the new features of Spring Boot 4.
  • Compile and test your project.
  • Fix any remaining issues. For example, for the various package changes (e.g. in Spring or Jackson), delete the broken import and let your IDE suggest the new one.

Another useful tool is the Spring Boot Properties Migrator. It can help you migrate your application.properties or application.yml files to Spring Boot 4.

To use it, add this dependency to your pom.xml after you upgraded to Spring Boot 4.0:

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

Then start the application and check the logs of PropertiesMigrationListener. It will print warnings for deprecated properties. Fix the changed properties, then remove the dependency when done.

Conclusion

In this article, I presented some of the main new features of Spring Boot 4, as well as some tips to migrate more efficiently with OpenRewrite. I hope this will make your Spring Boot 4 journey smoother.

I left aside the test features brought by Spring Boot 4. I may address this subject in a future article.

For additional information, you can refer to the Spring Boot 4 Release Notes or Reference Documentation.

© 2007-2026 Florian Beaufumé