Now that we're at Spring Boot 3.3, where are you? Over the past 3.x version updates, we've seen a host of new features that range from the ability to use new Java language features, enhanced observability, better support for native compilation with Graal, and support for Virtual Threads.
For many people, though, the upgrade to this latest version seems incredibly daunting, especially coming from older 2.x versions. After all, there are a lot of things that need to be updated aside from Spring such as Java versions and the move to Jakarta EE 9. And it’s likely that you have many different versions of the various dependencies running across your organization, which makes the task of migrating even more difficult.
Read on to learn how to make the software migration process smoother for your entire organization.
Why you should upgrade your services
It’s easy to take an approach of “if it’s not broken, why fix it?” However, OSS support for Spring Boot 2.x ended in November of 2023. This means you’ll need to make all of these upgrades to ensure that your software remains secure when new CVEs are published. Furthermore, by upgrading, you’ll get access to a host of new features across many tools.
For example, by upgrading to Java 17, which is a requirement for Spring Boot 3.0+, you’ll not only get a wide variety of new Java language features (records, pattern matching, switch expressions, etc.), but you’ll also benefit from performance improvements made to the virtual machine and garbage collector.
Note: Your environment will have a unique performance footprint. It is a good idea to use a monitoring platform to create a baseline of your applications' performance today, so you can quantify the savings when you upgrade.
In regards to Spring Boot 3.x, you can benefit from better support for building native executables and using the GraalVM. By building your Spring applications as native executables, you’ll find significant improvements in startup time. You’ll also find that observability has been a key theme in this new version, with tracing now being implemented via Micrometer Tracing.
From Spring Boot 3.2 onwards you can take advantage of Virtual Threads in Java 21 and up. These allow you to serve more requests from the same hardware by offloading blocking IO operations without complicating the programming model.
What you’ll need to update when migrating to Spring Boot 3.x
Moving to Spring Boot 3.x includes a number of associated migrations and dependency updates that you must do prior to migrating to this new Spring Boot version, including:
- Upgrade your organization’s applications, infrastructure, and CI/CD pipeline to use Java 17 or 21. The good news with this step is that this work can be performed prior to upgrading any of your Spring Boot applications.
- Any of your existing Spring applications that leverage Java EE will require an update to Jakarta EE 9. This may seem like a straightforward exercise that involves moving all imports from the `javax` namespace to the `jakarta` namespace but this also requires that any third-party libraries also be migrated to versions that are compatible with Jakarta EE 9.
- Finally, depending on which version of Spring Boot your applications are being migrated from, there may be several required changes to both the application’s code and configuration when moving to Spring Boot 3.x
Java 17 migration
Spring Boot/Spring require a baseline version of Java 17 and will necessitate applications to migrate to the latest long term support (LTS) version of the Java platform.
For those applications that are running on Java 8, the key sources of friction are the introduction of the module system in Java 9 and the removal of many of the J2EE javax dependencies from the core JDK. Applications that use any of the Java EE specifications will be required to add explicit dependencies to their projects. Additionally, there are many third-party dependencies that are not compatible with the Java module system that must be upgraded as part of this exercise.
Starting with Java 11 a new, six-month release cadence was adopted by the OpenJDK community and, with it, users must now deal with deprecated APIs. Deprecated features are initially non-fatal but are later removed from the Java platform. Applications that are upgrading across multiple versions of Java will have to address the API removed from the platform. Fortunately, there is a Java tool, Jdeprscan, that can be used to identify which APIs have been deprecated or removed from the platform. Applications that are using these APIs will be required to remediate their solutions to successfully upgrade to Java 17.
Java 21 and virtual threads
There’s also a convenient Spring Boot 3.x best practices recipe that allows you to upgrade to the latest Spring Boot 3.x version, as well as upgrade to Java 21 and the use of virtual threads. Going forward we’ll maintain this as a convenient recipe to get all the recommended recipes for Spring Boot applied at once.
As part of the upgrade to Java 21 we’ll also adopt methods from the new Sequenced Collections interface, as well as move away from now deprecated constructors for Locales and URLs, as well as some other smaller enhancements. See the Migrate to Java 21 recipe for the full details.
Jakarta EE 9 migration
The other big task that needs to be taken care of prior to the Spring Boot 3.x update is the migration to J2EE 9. This is significant due to the fact that the namespace for all Java EE libraries has changed from javax to jakarta.*. As part of that, any reference to these libraries must have their packages migrated to the new namespace.
Additionally, third-party libraries like Jackson, Hibernate, Tomcat, and Jetty provide alternate artifacts for compatibility with Jakarta EE 9. These new artifacts will require users of Sprint Boot to carefully review their third-party libraries to ensure that they have a compatible artifact.
Spring Boot migration
The 2.x line of Spring Boot was released in Feb 2018 and there have been seven minor version updates over that time. The Spring Boot team provides a migration guide for each version of Spring Boot where the most common issues are dealing with configuration changes, upgrading third-party dependencies and dealing with deprecated/removed APIs. Depending on which version of Spring Boot an application is currently running, the changes required to upgrade the application may be substantial.
Another factor that can be challenging for organizations is that an application may use multiple sub-projects from the Spring catalog (Spring Data, Spring Batch, Spring Integration, etc.) and each of these sub-projects have their own requirements when upgrading to newer versions.
How OpenRewrite auto-refactoring can help
The good news is that the OpenRewrite community is working on recipes for many of these complex changes. This means there will be significant portions of these updates that can be automated using OpenRewrite. For those who don’t know, OpenRewrite is a semantically-aware code search and transformation tool. It is capable of making sophisticated changes to code, build files, and configurations that are idiomatically consistent with the existing project's formatting standards.
In fact, OpenRewrite has a large catalog of recipes that can help users as they migrate to Spring Boot 3 including:
- Migrating from older versions of Java up to Java 17
- Migrating from Junit 4 to 5
- Migrating from Java EE to Jakarta EE 8 and 9
- Migrating from older versions of Spring Boot (1.5 -> 2.x, 2.7 ->3.0, 3.0 -> 3.x)
These recipes make coding changes, updates, and configuration changes!
Recipes are composable, which enables larger-scale framework migrations to consist of calling a set of recipes that perform specific tasks or chaining other composite recipes together. For example, the recipe to migrate to Java 11 can take advantage of recipes that modify Maven dependencies and recipes that target the migration of specific, deprecated APIs. In turn the recipe to migrate to Java 17 can be chained to the Java 11 recipe, and the Spring Boot 3.x migration recipe can leverage those recipes along with a recipe that migrates the Jakarta EE libraries to version 9.
You can also use this composable nature of recipes to target specific components you’re not up-to-date with if you want to reduce the scope of the changes you’re making. For instance without going to Spring Boot 3.x, you could:
- Migrate Spring Boot 2.x projects to JUnit 5 from JUnit 4
- Migrate to Spring Security 5.8
- Migrate to Java 17
- Perform Spring Boot 2.x best practices
Let’s walk through some of the types of changes that can be automated with OpenRewrite:
OpenRewrite can intelligently make changes to existing Maven build files to upgrade dependencies.
Coding changes are made safely because of OpenRewrite’s rich Lossless Semantic Tree (LST) model that allows new imports to be added (following the conventions of the existing source code) and code changes are indented properly.
And complex code changes can be made safely. In this example SimpleAuthConfig is modified to no longer extend WebSecurityConfigurerAdapter and the configure method is changed to return a new bean. The imports are adjusted, new imports are in the correct location, and formatting is consistent with the surrounding code.
There are hundreds of different recipes like these currently available on OpenRewrite, with more being added over time. It’s also possible to create your own custom recipes and then apply those recipes to your projects to perform safe, automated refactoring that will save you and your team hours of work that would normally have to be applied by hand.
Check out Moderne: migration engineering at scale
If you’re working in an enterprise environment with hundreds or thousands of repositories, manually running these recipes on each repository isn’t feasible or scalable. This is where Moderne, your code collaboration platform, can come in. The Moderne SaaS Platform enables you to run each of these recipes across all of your repositories in a matter of minutes—all without writing a single line of code. You can see the detailed composite Spring Boot 3.2 migration recipe list below (with many other recipes nested under those listed) that enables you to migrate from whatever your starting version is with a push of a button in the Moderne Platform.
You can also work in the multi-repo Moderne CLI to accelerate your migration.
Want to learn more?
You can join the OpenRewrite Slack channel or Discord server to ask questions and interact with the community that is developing all of these recipes. Or, check out the documentation which includes more information on how to get started and what OpenRewrite can do.
Also, contact Moderne to learn more about how you can use our platform with your own codebase.