You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Boot 3.1.4时Jakarta Servlet依赖版本冲突问题求助

Spring Boot 3.1.4升级时的Jakarta Servlet依赖冲突问题

问题描述

正在将服务升级到Spring Boot 3.1.4,遇到Jakarta Servlet版本依赖不匹配的问题:

  • 强制设置jakarta.servlet版本为5.0.0时,应用可正常启动,但测试用例加载失败
  • 强制设置版本为6.0.0时,测试用例可正常运行,但应用启动失败

当前build.gradle配置

plugins {
    id 'org.springframework.boot' version '3.1.4'
    id 'io.spring.dependency-management' version '1.0.12.RELEASE'
    id 'java'
    id 'eclipse'
    id "com.diffplug.eclipse.apt" version "3.38.0"
    id 'jacoco'
    id 'org.sonarqube' version '3.3'
}

group = 'com.app.company'
version = '1.1.0'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    all {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-websocket'
        //exclude group: 'commons-logging', module: 'commons-logging'
        testImplementation.exclude group: 'org.projectlombok', module: 'lombok'

        if (project.hasProperty('runTests')) {
            testImplementation.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
        }
        resolutionStrategy.eachDependency { details ->
            if (details.requested.group == 'jakarta.servlet') {
                // to support jetty on spring boot 3.1.4
                details.useVersion '6.0.0'
            }
        }
    }

}

repositories {
    mavenCentral()
}

dependencies {
    
    /* my configurations */

    testImplementation 'junit:junit:4.13.1'
    testImplementation'jakarta.servlet:jakarta.servlet-api:6.0.0'
    testImplementation 'com.h2database:h2:1.4.200'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
    testImplementation 'org.mockito:mockito-core:3.12.4'
}

test {
    useJUnitPlatform()
}

tasks.withType(JavaCompile) {
    configure(options) {
        options.compilerArgs << '-Xlint:deprecation' << '-Xlint:unchecked'
    }
}

jacoco {
    toolVersion = "0.8.10"
}

tasks.withType(Test) {
    jacoco.includeNoLocationClasses = true
    jacoco.excludes = ['jdk.internal.*']
}

test {
    useJUnitPlatform()
    finalizedBy jacocoTestReport
}

jacocoTestReport {
    dependsOn test
    reports {
        xml.required = false
        csv.required = false
        html.required = true
        // html.destination file("${rootProject.projectDir}/build/reports/jacoco")
        xml.destination file("${rootProject.projectDir}/build/reports/jacoco/report.xml")

    }
}


eclipse {
    jdt {
        apt {
            // You need to run gradle eclipseJdtApt eclipseJdt eclipseFactorypath
            // and restart vscode for this to take effect.
            genSrcDir = file('build/generated/sources/annotationProcessor/java/main')
        }
    }
}

报错详情

版本5.0.0时测试失败报错

java.lang.NoClassDefFoundError: jakarta/servlet/ServletConnection
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:210)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
    at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$8(ClassBasedTestDescriptor.java:363)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:368)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$9(ClassBasedTestDescriptor.java:363)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)

    ... 

版本6.0.0时应用启动失败报错

org.springframework.context.ApplicationContextException: Unable to start web server
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:164)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:602)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at com.app.company.CompanyApplication.main(CompanyApplication.java:22)
Caused by: java.lang.NoClassDefFoundError: jakarta/servlet/http/HttpSessionContext
    at org.eclipse.jetty.servlet.ServletContextHandler.newSessionHandler(ServletContextHandler.java:339)
    at org.eclipse.jetty.servlet.ServletContextHandler.getSessionHandler(ServletContextHandler.java:432)
    at org.eclipse.jetty.servlet.ServletContextHandler.relinkHandlers(ServletContextHandler.java:257)
    at org.eclipse.jetty.servlet.ServletContextHandler.<init>(ServletContextHandler.java:180)
    at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:301)
    at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:228)
    at org.springframework.boot.web.embedded.jetty.JettyEmbeddedWebAppContext.<init>(JettyEmbeddedWebAppContext.java:28)
    at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:159)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:183)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161)
    ... 6 more
Caused by: java.lang.ClassNotFoundException: jakarta.servlet.http.HttpSessionContext
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)

解决方案

1. 移除手动强制指定的Jakarta Servlet版本

Spring Boot的dependency-management插件已经维护了一套兼容的组件版本组合,手动指定版本会打破这种兼容性。删除configurations.all中的这段代码:

resolutionStrategy.eachDependency { details ->
    if (details.requested.group == 'jakarta.servlet') {
        // to support jetty on spring boot 3.1.4
        details.useVersion '6.0.0'
    }
}

2. 引入兼容的Jetty Starter

你已经排除了Tomcat Starter,必须确保引入Spring Boot提供的Jetty Starter来获取适配的Jetty版本:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-jetty'
    // 保留你的其他业务依赖
}

3. 删除手动添加的测试级Servlet API依赖

Spring Boot的spring-boot-starter-test已经包含了适配的Servlet API,手动添加会导致版本冲突,删除:

testImplementation'jakarta.servlet:jakarta.servlet-api:6.0.0'

4. 对齐测试框架版本

改用Spring Boot的测试starter来管理JUnit等依赖版本,避免手动指定版本带来的冲突:

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // 移除以下手动指定的版本
    // testImplementation 'junit:junit:4.13.1'
    // testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
    // testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
    testImplementation 'com.h2database:h2:1.4.200'
    testImplementation 'org.mockito:mockito-core:3.12.4'
}

5. 清理缓存并重新构建

执行命令清理构建缓存,确保新的依赖配置生效:

./gradlew clean build

核心原因

  • Spring Boot 3.1.x默认适配Jakarta Servlet 6.0,但需要搭配对应版本的Jetty(Jetty 11+适配Servlet 5.0,Jetty 12+适配Servlet 6.0)
  • 手动指定Servlet版本会导致Jetty与Servlet API版本不兼容:Servlet 6.0移除了HttpSessionContext,旧版本Jetty仍会尝试加载该类,引发启动失败
  • 测试框架需要的ServletConnection类仅在Servlet 6.0中存在,因此版本5.0.0会导致测试失败

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:25:55