Upgrading Spring Boot with OpenRewrite

Neethamadhu Madurasinghe
Sysco LABS Sri Lanka
6 min readApr 22, 2024
Generated using Leonardo AI

“Spring, ascending to a superior version, akin to the Phoenix’s rise from ash.”

Spring Framework is a popular open-source framework used to create reliable, production-ready applications using Java language. This framework comes with many useful modules such as Spring MVC, Spring Security, Spring JDBC, and Spring ORM. Spring Boot is a tool built on top of Spring Framework to simplify the development process and reduce boilerplate configurations. Therefore, Spring Boot is a perfect tool for developing microservices and REST APIs.

Why Upgrade Spring Boot?

Although it is completely possible to keep your old Spring Boot applications as they are, upgrading them to the latest version comes with many benefits.

Security — Older versions of Spring Boot and supported libraries could have unseen vulnerabilities for years and these vulnerabilities are fixed in later versions of Spring Boot. For example, CVE-2023–20883: Spring Boot Welcome Page DoS Vulnerability shows a vulnerability found in Spring Boot, and newer versions get shipped with solutions for these.

Maintainability — Older versions of these frameworks might have deprecated features. When your projects are using these outdated features, new developers might find it frustrating to deal with these projects.

Performance — Newer versions of Java and Spring Boot may include performance optimizations so that applications run faster while consuming less memory and computation. This can cut down costs drastically in the long run.

New features — It is always best to have new features and get rid of older tech. For example, Java 21 and Spring Boot 3.2 support Virtual threads and GraalVM native image support.

OpenRewrite

OpenRewrite is a framework that enables large-scale distributed source code refactoring for migrations, vulnerability patches, and API migrations. While the initial focus was on the Java language, now it has recipes for various languages and libraries including Python, Kotlin, XML, Spring, and OkHttp. In a nutshell, OpenRewrite is a way to programmatically refactor the code.

Recipes

In the context of OpenRewrite, “recipes” often refer to predefined sets of instructions or configurations. These instructions are just Java programs that perform specific tasks or transformations on code to achieve the desired output.

These recipes can be used in a few ways including Gradle plugins, Maven POM configuration, and Modern CLI. This document discusses how to use OpenRewrite recipes as Gradle plugins.

Upgrading Spring Boot with OpenRewrite

OpenRewrite provides a set of recipes for upgrading Spring boot applications from any older version to 3.2 which is the latest at the moment. However, it is wise to perform this upgrade process step by step to make sure the application works properly in each stage. For example, if the target version is 3.2 and the current version is 3.0, then upgrading to 3.1 first could be better because the code might give errors during the 3.1 to 3.2 upgrade process.

Steps to upgrade Spring Boot

Before initiating the upgrade, ensure that your program works correctly with its current configurations. Verify its stability, documenting any pre-existing problems for reference. This proactive approach will make it easier to identify and address issues introduced by the upgrade process, providing a smoother transition.

To begin the Spring Boot upgrade process, it is essential to modify your build.gradle file with the necessary dependencies. Depending on the targeted Spring Boot version, adjustments might be required. Detailed Gradle modifications for Spring Boot 2.6 can be found in this link.

Figure 1: Modified build.gradle file. Currently using Spring Boot 2.6.

After adjusting the dependencies, execute the following command:

gradle rewriteRun

This will initiate the upgrading process to the latest Spring Boot version.

It’s crucial to be mindful of potential issues during this step, particularly with Gradle and Java compatibility. Ensure that you are using a supported version of both. In a recent project upgrade to Spring Boot 2.7, I experienced success only with Gradle version 7.6 and Java 17. Attempting to use even the latest versions (Gradle 8.5 and Java 21) resulted in errors while running the OpenRewrite recipe. However, once you find the correct Gradle version, the script takes only a small time to execute and migrate the code.

Figure 2: Terminal after a successful recipe execution. OpenRewrite recipe took less than 2 minutes to upgrade a Spring Boot application with 10,000 lines of code.
Figure 3: Spring Boot version is upgraded to 2.7.

Once the OpenRewrite recipe is done with the upgrading process, the first thing to do is make sure your code is not broken. Execute all unit tests and run the application to identify and address any potential breakages introduced during the upgrade. It’s common for upgrade processes to bring changes that may affect project stability.

If unit tests fail, the first step is to examine and fix the issues in those tests. In my experience, OpenRewrite recipes can alter unit tests, particularly in cases involving dependency injections within test classes. Another common scenario is issues related to external packages. OpenRewrite may fail to track down changes in newer versions of some packages. In addition to those, common classes, including those annotated with @ControllerAdvice , may also undergo breaking changes (Figure 4 shows an example and how to resolve this issue). Therefore you have to fix those by referring to the corresponding documentation. There is no common method to do this and should be done according to the packages you are using. So make sure you fix any issue related to your code before moving into the next step.

Figure 4 a: Previously overridden method is showing an error.
Figure 4 b: Parameter types should be changed.

Note: If you notice there are changes in things like method names and variable names, chances are OpenRewrite is doing those to follow current best practices. Therefore it is best to keep those as they are.

Once the code is fixed, focus on cleaning up the build.gradle file. There will be newly added dependencies, removed dependencies, and modified dependencies. Carefully go through these and remove unnecessary ones. However, avoid rolling back modifications to existing dependencies, as they are essential. If you plan to further upgrade Spring Boot, consider performing this cleaning process after all upgrade stages are completed.

Figure 5: Unnecessary dependencies that should be removed.
Figure 6: Not many manual changes are needed.

If you manage to go through these steps successfully, you will end up with a project that runs an upgraded version of Spring Boot. However, this process might not be as smooth as it sounds since some old code bases tend to throw various kinds of errors such as hibernate-related exceptions after Spring Boot upgrade, and resolving these issues might take a lot of time and effort. For example, a Spring Boot application I upgraded recently required about 20 hours of Research on Java packages and manual changes to get it compiled and run on Java 21 and Spring Boot 3.2. Regardless of these obstacles, it is always best to have the latest possible version of frameworks and libraries for optimal security, performance, and maintainability.

--

--