Migrating Spring Boot 2.x (JDK 1.8) to Spring Boot 3.x (JDK 21)

  • Spring Boot 3.2.5
  • JDK 21

Key Changes and Migration Steps

1. Namespace Shift: javax to jakarta

Spring Boot 3 and Spring Framework 6 mandate a migration from the javax.* namespace to the jakarta.* namespace for specifications like Servlet and JPA. This change is a consequence of the Java EE platform's evolution under the Eclipse Foundation, now branded as Jakarta EE.

Action Required:

  • Update Imports: Globally replace import javax.* statements with their corresponding import jakarta.* equivalents. For example, change import javax.servlet.* to import jakarta.servlet.*.
  • Add Jakarta Annotations Dependency: Ensure you have the jakarta.annotation-api dependency.

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.0</version>
</dependency>
            

2. Spring Framework Specifics

@PathVariable Parameter Names:

In Spring Boot 3, the @PathVariable annotation requires explicit specification of the parameter name. The framework no longer infers it automatically, especially when parameter names are not preserved during compilation.

Action Required: Ensure all @PathVariable annotations include the value attribute.


// Incorrect:
@GetMapping("/projects/{projectCode}")
public ResponseEntity<?> getProject(@PathVariable Long projectCode) { ... }

// Correct:
@GetMapping("/projects/{projectCode}")
public ResponseEntity<?> getProject(@PathVariable(value = "projectCode") Long projectCode) { ... }
   

3. Mybatis-Plus Dependency Update

Mybatis-Plus requires a version update, and its starter dependency artifact ID has changed.

Action Required:

  • Update to Mybatis-Plus version 3.5.5 or later.
  • Change the Maven dependency coordinate from mybatis-plus-boot-starter to mybatis-plus-spring-boot3-starter.

<!-- Mybatis-Plus -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
  <version>3.5.5</version>
</dependency>
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus</artifactId>
  <version>3.5.5</version>
</dependency>
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-annotation</artifactId>
  <version>3.5.5</version>
</dependency>
   

4. Hibernate Validator Compatibility

Upgrading to Spring Boot 3 may lead to issues with older versions of Hibernate Validator, manifesting as errors like java: 无法访问javax.validation.ConstraintValidator.

Action Required: Update the Hibernate Validator dependency to a compatible version, such as 8.0.1.Final.


<dependency>
   <groupId>org.hibernate.validator</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>8.0.1.Final</version>
</dependency>
   

5. Lombok Compatibility

Incompatibility issues with Lombok can arise, potentially causing errors like java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field ‘com.sun.tools.javac.tree.JCTree qualid’.

Action Required: Ensure you are using Lombok version 1.18.30 or later.


<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.30</version>
 <scope>provided</scope>
</dependency>
   

6. Logging Configuration (Logback)

Version conflicts or misconfigurations with Logback can lead to errors like java.lang.AbstractMethodError: Receiver class org.springframework.boot.logging.logback.RootLogLevelConfigurator does not define or inherit an implementation of the resolved method ‘abstract void configure(ch.qos.logback.classic.LoggerContext)’ of interface ch.qos.logback.classic.spi.Configurator. or SLF4J warnings about missing providers.

Action Required: Align your SLF4J and Logback dependencies to compatible versions. Using the versions specified in the pom.xml example below is recommended.


<!-- SLF4J -->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>2.0.13</version> <!-- Or a later compatible version -->
</dependency>
<!-- Use a binding that matches your logging framework, e.g., logback-classic -->
<!-- If using logback, slf4j-log4j12 is usually not needed -->

<!-- Logback -->
<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.5.6</version> <!-- Or a later compatible version -->
</dependency>
<dependency>
 <groupId>ch.qos.logback</groupId>
 <artifactId>logback-core</artifactId>
 <version>1.5.6</version> <!-- Or a later compatible version -->
</dependency>
   

Ensure that only one SLF4J binding is present and compatible with your chosen logging framework (e.g., Logback).

7. Handling Unresolved Parameters in Exception Handlers

An error like java.lang.IllegalStateException: Could not resolve parameter [1]... No suitable resolver in exception handlers often occurs when method parameter names are not available at runtime. This is exacerbated by compile-time settings that strip parameter names.

Action Required:

  • Ensure your Java compiler is configured to retain parameter names (e.g., using the -parameters flag during compilation).
  • For methods that require parameter resolution, explicitly use annotations like @RequestParam or @RequestBody to define the parameter's binding.

Recommended Project Structure (Dependancy Management)

To streamline version management and ensure consistency across dependencies, consider using a dedicated module (e.g., a Bill of Materials - BOM) to manage external dependency versions.

Example pom.xml Snippet for Dependency Management


<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <spring-boot.version>3.2.5</spring-boot.version>
   <mysql-connector.version>8.0.33</mysql-connector.version>
   <mybatis-plus.version>3.5.5</mybatis-plus.version>
   <slf4j.version>2.0.13</slf4j.version>
   <logback.version>1.5.6</logback.version>
   <jackson.version>2.16.1</jackson.version>
   <commons-lang3.version>3.12.0</commons-lang3.version>
   <commons-collections4.version>4.4</commons-collections4.version>
   <guava.version>33.0.0-jre</guava.version>
   <hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
   <commons-io.version>2.11.0</commons-io.version>
   <jakarta.annotation.version>2.1.0</jakarta.annotation.version>
</properties>

<!-- Dependency Management Section -->
<dependencyManagement>
   <dependencies>
       <!-- Spring Boot Parent POM -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-parent</artifactId>
           <version>${spring-boot.version}</version>
           <type>pom</type>
           <scope>import</scope>
       </dependency>

       <!-- Spring Boot Starters -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId>
           <version>${spring-boot.version}</version>
       </dependency>
       
       <!-- Validation -->
       <dependency>
           <groupId>org.hibernate.validator</groupId>
           <artifactId>hibernate-validator</artifactId>
           <version>${hibernate-validator.version}</version>
       </dependency>

       <!-- Google Guava -->
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>

       <!-- MySQL Connector -->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>${mysql-connector.version}</version>
           <scope>runtime</scope>
       </dependency>

       <!-- Mybatis-Plus -->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-annotation</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>

       <!-- SLF4J API -->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>${slf4j.version}</version>
       </dependency>
       
       <!-- Logback -->
       <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-classic</artifactId>
           <version>${logback.version}</version>
       </dependency>
       <dependency>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-core</artifactId>
         <version>${logback.version}</version>
       </dependency>

       <!-- Apache Commons -->
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
           <version>${commons-lang3.version}</version>
       </dependency>
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-collections4</artifactId>
           <version>${commons-collections4.version}</version>
       </dependency>
       <dependency>
           <groupId>commons-io</groupId>
           <artifactId>commons-io</artifactId>
           <version>${commons-io.version}</version>
       </dependency>

       <!-- Jackson -->
       <dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-annotations</artifactId>
           <version>${jackson.version}</version>
       </dependency>
       <dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-databind</artifactId>
           <version>${jackson.version}</version>
       </dependency>
       <dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-core</artifactId>
           <version>${jackson.version}</version>
       </dependency>

       <!-- Jakarta Annotations -->
       <dependency>
           <groupId>jakarta.annotation</groupId>
           <artifactId>jakarta.annotation-api</artifactId>
           <version>${jakarta.annotation.version}</version>
       </dependency>

   </dependencies>
</dependencyManagement>
   

Tags: spring-boot java JDK21 migration jakarta-ee

Posted on Mon, 26 Oct 2026 16:40:32 +0000 by bdlang