
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.
Background
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.
Docs
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.
- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-java-tools
- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphiql-spring-boot-starter
- https://mvnrepository.com/artifact/com.zhokhov.graphql/graphql-datetime-spring-boot-starter
- https://mvnrepository.com/artifact/com.graphql-java/graphql-java-extended-scalars
- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-spring-boot-starter
- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-spring-boot-starter-test
- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-webclient-spring-boot-starter
Well, that resolved some of the compilation errors.
HttpSecurity
As the Spring Security is also upgraded, the following statement is full of warnings.
httpSecurity
.formLogin().disable()
.authorizeRequests().anyRequest().permitAll().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy())
.and().cors()
.and().csrf().disable();
We spent some time mastering the new way of leveraging the configurer as the following.
httpSecurity
.formLogin(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.anyRequest()
.permitAll())
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()))
.cors(withDefaults())
.csrf(AbstractHttpConfigurer::disable);
However, the unit test always got 403. After hours of struggling with this issue, we finally found that there’s a breaking change to .requestMatchers().antMatchers(...)
.
httpSecurity
- .requestMatchers()
- .antMatchers("/actuator/**", "/graphiql**")
+ .securityMatchers(securityMatchers -> securityMatchers
+ .requestMatchers("/actuator/**", "/graphiql**"))
+ .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
+ .anyRequest()
+ .permitAll())
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.MySQL5Dialect
or org.hibernate.dialect.MySQL8Dialect
needs to be updated to org.hibernate.dialect.MySQLDialect
.
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.
Jakarta EE
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;
Other Dependencies
There are some other dependencies that require an upgrade as well. E.g.
- Antlr
- Elasticsearch
- Junit
- …
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.
runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")
Luckily, we don’t see any output from this dependency in the end.
Summary
With tens of hours tedious investigation, the project was successfully upgraded. Hope this article help you ease the migration.