This guide documents key technical adjustments required when modernizing a legacy Java application—from JDK 1.8 and Spring Boot 2.x to JDK 21 and Spring Boot 3.2+. The migration involves breaking API changes, dependency updates, and configuration refactoring across multiple layers: servlet stack, security, persistence, serialization, and tooling.
Prerequisites
- JDK 21 installde (LTS)
- Maven 3.9.0 or newer
- IDE configured for Java 21 language level (e.g., IntelliJ: File → Project Structure → Project SDK & Language Level → 21)
Core Build Configuration Updates
In pom.xml, align compiler and platform versions:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<java.version>21</java.version>
<spring-boot.version>3.2.4</spring-boot.version>
</properties>
Servlet Namespace Migration
Spring Boot 3 adopts the Jakarta EE 9+ namespace. Replace all javax.servlet.* imports with jakarta.servlet.*:
javax.servlet.http.HttpServletRequest→jakarta.servlet.http.HttpServletRequestjavax.servlet.Filter→jakarta.servlet.Filter
Remove any javax.* dependencies from pom.xml; they’re incompatible and will cause NoClassDefFoundError.
Spring Security 6 Refactor
The WebSecurityConfigurerAdapter class is removed. Replace it with functional bean registration:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(Customizer.<HttpBasicConfigurer>nullSafe())
.formLogin(Customizer.<FormLoginConfigurer<HttpSecurity>>nullSafe())
.logout(Customizer.<LogoutConfigurer<HttpSecurity>>nullSafe())
.exceptionHandling(ex -> ex
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.accessDeniedHandler(new RestfulAccessDeniedHandler()))
.authorizeHttpRequests(authz -> authz
.requestMatchers(HttpMethod.GET, "/actuator/**", "/swagger-ui/**").permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated());
return http.build();
}
}
Note: authorizeHttpRequests() replaces deprecated antMatchers(). Rule ordering matters—anyRequest().authenticated() must be last.
MyBatis Integration Fixes
Upgrade MyBatis integrations to Spring Boot 3–compatible versions:
- Use
mybatis-spring3.0.3 (not older versions) - Exclude transitive
mybatis-springfrommybatis-plus-boot-starterif used
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
Ensure SqlSessionFactoryBean or SqlSessionTemplate is explicitly declared if using custom configuration.
JWT Token Handling
Legacy javax.xml.bind.DatatypeConverter is removed in JDK 21. Migrate to modern JWT libraries:
- Replace
jjwt-api:0.11.xwithio.jsonwebtoken:jjwt-api:0.12.5 - Update signing/verification logic to use
Jwts.builder()andJwts.parserBuilder()
API Documentation Repllacement
Springfox is incompatible with Jakarta EE. Switch to springdoc-openapi or knife4j:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
Update permitted endpoints in security rules: e.g., permit /v3/api-docs/** and /swagger-ui/**.
Redis Serialization Update
Replace deprecated setObjectMapper() with RedisSerializationContext-based configuration:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setDefaultSerializer(RedisSerializer.json());
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.json());
template.setHashKeySerializer(RedisSerializer.string());
template.setHashValueSerializer(RedisSerializer.json());
return template;
}
Build Tooling Notes
- If Maven fails with
invalid flag: --releasein IDE, runmvn clean compiledirectly in terminal — this often bypasses misconfigured IDE Maven runner flags. - Verify that no third-party plugins rely on internal JDK APIs (e.g.,
com.sun.tools.javac). Upgrade or replace such plugins.
Data Access Layer Adjustments
For Spring Data modules:
- Elasticsearch: Downgrade
spring-data-elasticsearchto4.4.xifElasticsearchRepository.deleteAll()is missing. - MongoDB: Pin
spring-data-mongodbto4.1.5(or latest 4.x compatible with Spring Boot 3.2) to resolveListCrudRepositoryresolution failures. - MySQL: Append
?allowPublicKeyRetrieval=true&useSSL=falseto JDBC URL if encounteringPublic Key Retrieval is not allowed.