SPRING 3.0 MIGRATION OVERVIEW
Spring Boot 3.0 has just been released, packed with exciting new features such as advanced observability, better support for native compilation with Graal, and the ability to utilize the latest Java language features. However, for many individuals, upgrading to this latest version may appear overwhelming. The reason being, it requires the updating of many other aspects, such as the Java versions and a transition to Jakarta EE 9. Additionally, given the probability of your organization running several different dependency versions, the migration process can become even more complicated.
It is common to adopt an approach that revolves around the ideology of “if it’s not broken, why fix it?” However, it is important to note that the support for Spring Boot 2.x is scheduled to end by the end of 2023. Additionally, upgrading to the latest version of Spring Boot will provide you with access to a range of new features available across different tools. For instance, upgrading to Java 17, a prerequisite for Spring Boot 3.0, will not only grant you access to a variety of new Java language features (such as pattern matching, records, switch expressions, etc.), but it will also improve the performance of the virtual machine and garbage collector.
MAIN STEPS
Migrating to Spring Boot 3.0 necessitates a series of associated migrations and dependency updates that must be carried out before upgrading to this latest version of Spring Boot. These include:
• Updating your organization’s applications, infrastructure, and CI/CD pipeline to use Java 17. The upside to this step is that it can be done before updating any of your Spring Boot applications.
• Any pre-existing Spring applications that utilize Java EE will require an update to Jakarta EE 9. While this may seem like a simple task of moving all imports from the javax namespace to the jakarta namespace, it also requires the migration of any third-party libraries to versions that are compatible with Jakarta EE 9.
import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; -> import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid;
There are instances when we may not need to refactor package names when upgrading our software, as some dependencies such as javax.sql.DataSource
already belong to JDK 17, not Jakarta EE. In these cases, it is not necessary to make any changes to the package name, as it is already consistent with the latest version of our app. It is important to note, however, that it is essential to thoroughly review and test all dependencies to ensure that they are compatible with the new version of the code.
• Lastly, depending on which version of Spring Boot your applications are being migrated from, there may be a need to make several required changes to both the application’s code and configuration when moving to Spring Boot 3.0.
It is crucial to take note of these steps, as they are all essential requirements that must be fulfilled before upgrading. Sometimes we need to just upgrade dependency version, sometimes we have to migrate to a new package. For example:
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.11</version> </dependency> -> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.0.0</version> </dependency>
POST MIGRATION ISSUES – COMMON EXAMPLES
#1
Now let’s look at a situation where we want to update a library used by other applications as a Maven dependency.
Parameter 0 of constructor in com.some.package.a.Class required a bean of type 'com.some.package.b.SomeExternalClass' that could not be found.
There is a good chance that exporting configuration files is the cause. Before 3.0, Spring Boot checked for the presence of a META-INF/spring.factories
file within your published jar. The file should list your configuration classes under the EnableAutoConfiguration
key. Now, instead of providing configuration in META-INF/spring.factories, you would do so in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=pl.inero.commons.email.EmailServiceConfiguration -> pl.inero.commons.email.EmailServiceConfiguration
We’llonfiguration properties for Redis have moved from spring.redis. to spring.data.redis. as redis auto-configuration requires Spring Data to be present on the classpath. Also configuration Properties for Cassandra have moved from spring.data.cassandra. to spring.cassandra..
Now let’s take some Spring Security related issue.
#2
Invalid CSRF token found for http://localhost:8080/api/test Responding with 403 status code
When using Spring Security, it’s important to be aware of changes between different versions of the framework. For example, in Spring Security version 5, the CsrfToken is loaded by default on each request, which means that even unnecessary requests must have the HttpSession read in a typical setup. However, the default behavior of Spring Security 6 is to postpone the lookup of the CsrfToken until it is actually required.
In our particular application, the CsrfToken is required for every request, so we need to make sure that we specifically opt into the 5.8 defaults to ensure that the CsrfToken is loaded on every request, as it was in version 5. This is a necessary step to ensure that our application runs as intended and that we do not encounter any issues related to the CsrfToken during runtime.
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http / ... / .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) / ... / } -> @Bean DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception { CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler(); requestHandler.setCsrfRequestAttributeName(null); return http / ... / .csrf((csrf) -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(requestHandler) / ... / }
#3
Cannot resolve method 'antMatchers()' (...)
Methods like antMatchers() (as well as mvcMathcers() and regexMatchers()) have been deprecated and removed with Spring Security 6.0 (have a look at this link if you wonder why: Deprecate trailing slash match). Overloaded method requestMatchers() was provided as a uniform mean for securing requests. It facilitates all the functionality of the configuration methods that have been removed from the API.
Also, method authorizeRequests() has been deprecated and shouldn’t be used anymore. A recommended replacement – authorizeHttpRequests() (you can find more information regarding these changes here). Here we have sample code and how it could have been changed:
http.requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher(API_KEY_URI_MATCHER))) .httpBasic().and() .authorizeRequests() .antMatchers("/ping").permitAll() .anyRequest().authenticated(); -> http.securityMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher(API_KEY_URI_MATCHER))) .httpBasic().and() .authorizeHttpRequests() .requestMatchers("/ping").permitAll() .anyRequest().authenticated();
ON CONFIGURATION PROPERTIES
When upgrading to Spring Boot 3.0, it is essential to update the configuration properties in the application.properties
or application.yml
files to account for any changes or removals of properties. Spring Boot provides a convenient tool called spring-boot-properties-migrator
to help with this migration. By adding this module as a dependency to our project, it will migrate the properties temporarily at runtime and analyze the application’s environment to print diagnostics at startup.
To add the spring-boot-properties-migrator
module to our project, we need to add the following dependency to the pom.xml
file.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-properties-migrator</artifactId> <version>3.0.0</version> <scope>runtime</scope> </dependency>
This will ensure that our properties are migrated correctly and that we can benefit from any diagnostics provided by the module during startup.
As always, more tips on migration can be found in the Spring documentation. Check it out now.
In Inero Software we build digital products for digital logistics of tomorrow. We connect active aproach to analysis of business needs and combine with cutting edge technologies and AI/ML. In the blog post section you will find other articles about IT systems and more!