升级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
相关产品推荐
相关产品推荐

