Spring Boot is a popular framework for building Java-based web applications. It provides a powerful set of tools and features that make it easy to develop, test, and deploy applications quickly and efficiently. With the release of Spring Boot 3.1, there are several new features and improvements that developers can take advantage of. However, migrating from Spring Boot 2.7 to 3.1 can be a daunting task, especially if you have a large codebase. In this article, I will share with you my migration story in hope of providing guidance on how to migrate your application successfully.
The project to be migrated is a giant PaaS + SaaS cloud native project on JDK 11, Kotlin 1.6, Gradle 7 with Kotlin DSL, Spring Boot 2.7, K8s and with quite a bunch of other legacy dependencies.
Our goal is to migrate to JDK 17 and Spring Boot 3.1 so that in the future we will be ready for the native image.
Before the migration begins, I strongly recommend reading the official Spring Boot 3.0 Migration Guide as it covers the solutions to the majority of the issues I met during the migration.
Quick and Easy Start, But
It’s quite straight forward to update the gradle build scripts with the following changes (Thanks to the previous upgrade to Spring Boot 2.7.).
- JDK 17
- Gradle 8.1
- Kotlin 1.8
However, there were tons of compilation errors. The long journey of fighting with those errors began.
GraphQL Java Kickstart
We heavily use GraphQL as the backend API as it’s much more expressive than the RESTful API is, and it provides quite decent input validation out-of-the-box. However, Spring Boot 3.1 outputs weird error messages during compilation and application initialization. The first thing to do was to upgrade GraphQL related dependencies to the latest ones.
Well, that resolved some of the compilation errors.
As the Spring Security is also upgraded, the following statement is full of warnings.
We spent some time mastering the new way of leveraging the configurer as the following.
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.sessionManagement(sessionManagement -> sessionManagement
However, the unit test always got 403. After hours of struggling with this issue, we finally found that there’s a breaking change to
- .antMatchers("/actuator/**", "/graphiql**")
+ .securityMatchers(securityMatchers -> securityMatchers
+ .requestMatchers("/actuator/**", "/graphiql**"))
+ .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
The root cause was we thought the
requestMatchers(...) should be inside the
authorizeHttpRequests(). Actually, it's not. Thus, all requests were denied with 403.
After the patch was in place, the GraphQL worked.
MySQL JDBC Driver
Quote from the official guide is quite handy. However, it didn’t mention
org.hibernate.dialect.MySQL8Dialect needs to be updated to
The coordinates of the MySQL JDBC driver have changed from mysql:mysql-connector-java to com.mysql:mysql-connector-j.
Redisson Spring Data
Although https://mvnrepository.com/artifact/org.redisson/redisson-spring-data-30 is the latest one, it works with Spring Boot 3.1 well.
Quote from the official guide:
Whenever Spring Boot depends on a Jakarta EE specification, Spring Boot 3.0 has upgraded to the version that is included in Jakarta EE 10. For example, Spring Boot 3.0 uses the Servlet 6.0 and JPA 3.1 specifications.
That change contributes to the changes in thousands of files.
- import javax.annotation.Resource;
+ import jakarta.annotation.Resource;
- import javax.persistence.Converter;
+ import jakarta.persistence.Converter;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.Index;
- import javax.persistence.Table;
+ import jakarta.persistence.Column;
+ import jakarta.persistence.Entity;
+ import jakarta.persistence.Index;
+ import jakarta.persistence.Table;
- import javax.persistence.criteria.CriteriaBuilder;
- import javax.persistence.criteria.Expression;
- import javax.persistence.criteria.Predicate;
+ import jakarta.persistence.criteria.CriteriaBuilder;
+ import jakarta.persistence.criteria.Expression;
+ import jakarta.persistence.criteria.Predicate;
There are some other dependencies that require an upgrade as well. E.g.
Configuration Properties Migration
According to the official guide, the following Once added as a dependency to your project, this will not only analyze your application’s environment and print diagnostics at startup, but also temporarily migrate properties at runtime for you.
Luckily, we don’t see any output from this dependency in the end.
With tens of hours tedious investigation, the project was successfully upgraded. Hope this article help you ease the migration.