Spring Boot 3.0 marks a significant upgrade, introducing new features and enhancements. However, it mandates Java 17 as the minimum version and presents several compatibility challenges. This guide aims to help you navigate the migration process smoothly.
Benefits
• Upgrading to Java 17
To use Spring Boot 3.0, you must upgrade to JDK 17. This upgrade allows you to leverage the latest features and performance improvements in Java 17.
• Support for GraalVM Native Image
GraalVM Native Images offer a novel method to deploy and run Java applications, providing benefits such as instantaneous startup and reduced memory usage, which are significant pain points for Spring Boot applications.
• Enhanced Observability with Micrometer and Micrometer Tracing
These tools offer improved observability features. More details can be found on the official documentation.
Drawbacks
• Time and Resource Requirements
Migrating to a new major release demands substantial time and resources, especially for thorough testing. This process impacts all your application flows and requires comprehensive testing. While updating code might take a few days, testing can extend over a week or more, depending on your project's complexity.
• Potential for New Bugs
Since the migration impacts all application flows, insufficient test coverage might lead to missed bugs. Ensure extensive end-to-end testing and scrutinize logs for any new exceptions or discrepancies compared to the previous state.
Preparation Steps
1. Upgrade to Spring Boot 2.7 First
If you're using an earlier version of Spring Boot, upgrade to Spring Boot 2.7 first to minimize compatibility issues during the migration to 3.0.
2. Review Dependencies
Examine your dependencies and dependency management for Spring Boot 3.x to assess their impact on your project. For dependencies not managed by Spring Boot, identify compatible versions before upgrading.
3. Address Deprecations
Classes, methods, and properties deprecated in Spring Boot 2.x have been removed in this release. Ensure your code does not call any deprecated methods before upgrading.
Migration Steps
1. Configuration Properties Migration
Add the following dependency to your Maven pom.xml to include the Spring Boot Properties Migrator:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
This tool will analyze your application's environment and print diagnostics in the startup console logs, guiding you on how to update your properties accordingly.
2. Update Dependencies
Update your pom.xml with the new parent POM and Java version:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version>
</properties>
Avoid specifying versions for Spring Data JPA, Spring Web, Spring Data Redis, etc., as their compatible versions are declared in the parent POM.
If you are using any of these dependencies, update to the following versions to ensure compatibility:
• MySQL Connector:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>
• Logback:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
• OpenFeign:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.0.4</version>
</dependency>
• Spring JDBC:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.12</version>
</dependency>
• Jackson Databind:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.3</version>
</dependency>
• Redisson:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.24.3</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-31</artifactId>
<version>3.24.3</version>
</dependency>
• Mockito:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
• JJWT:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.3</version>
</dependency>
• Springdoc OpenAPI:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
• Spring Kafka:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.0.10</version>
</dependency>
3. Rebuild and Modify Code
After updating dependencies, rebuild your code with mvn clean package. You'll encounter issues with javax imports, which need to be replaced with jakarta.
• Hibernate Configuration
In Hibernate 6, use MySQLDialect for all MySQL versions. Update your application properties as follows:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
4. Update Spring Security Configuration
If you're using WebSecurityConfigurerAdapter, you'll need to migrate to a lambda-based configuration.
• Old Configuration:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// Configuration code
}
• New Configuration:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig {
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.cors(AbstractHttpConfigurer::disable).csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(auth -> auth.requestMatchers(AUTH_WHITELIST).permitAll().anyRequest().authenticated());
http.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.exceptionHandling(ex -> ex.authenticationEntryPoint(authEntryPointJwt));
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
• JJWT Library Updates
Update your JJWT code to the new API format.
• Old Code:
public String generateJwtToken(Long userId) {
return Jwts.builder()
.setSubject(userId.toString())
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS256, jwtSecret.getBytes(StandardCharsets.UTF_8))
.compact();
}
• New Code:
public String generateJwtToken(Long userId) {
return Jwts.builder()
.subject(userId.toString())
.issuedAt(new Date())
.expiration(new Date(new Date().getTime() + jwtExpirationMs))
.signWith(Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8)), Jwts.SIG.HS256)
.compact();
}
Final Steps
Rebuild your project and test thoroughly. Carefully verify all APIs for discrepancies or exceptions, monitoring logs throughout the process.
Conclusion
Migrating to Spring Boot 3.0 and JDK 17 is a complex but manageable process. It involves addressing several compatibility issues and updating numerous dependencies. Thorough testing is crucial to ensure a smooth transition.
The only way to make sense out of change is to plunge into it, move with it, and join the dance. - Alan Watts
Top comments (0)