Spring Boot Web依赖Tomcat引发NoSuchMethodError及替换方案问询
问题描述
在使用Spring Boot 2.7.15版本时,同时引入spring-boot-starter-web和spring-boot-starter-webflux依赖后,出现NoSuchMethodError错误,具体错误日志如下:
2023-09-11T10:48:54,424 INFO [http-nio-8082-exec-1] log: Initializing Spring DispatcherServlet 'dispatcherServlet' 2023-09-11T10:48:54,425 INFO [http-nio-8082-exec-1] initServletBean: Initializing Servlet 'dispatcherServlet' 2023-09-11T10:48:54,425 INFO [http-nio-8082-exec-1] initServletBean: Completed initialization in 0 ms 2023-09-11T10:48:54,453 ERROR [http-nio-8082-exec-2] log: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.getHttpServletMapping()Ljavax/servlet/http/HttpServletMapping; at org.apache.catalina.core.ApplicationHttpRequest.setRequest(ApplicationHttpRequest.java:681) at org.apache.catalina.core.ApplicationHttpRequest.<init>(ApplicationHttpRequest.java:103) at org.apache.catalina.core.ApplicationDispatcher.wrapRequest(ApplicationDispatcher.java:857) at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:570) at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:558) at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:563) at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:337) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:237) at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:242) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:834) 2023-09-11T10:48:54,455 ERROR [http-nio-8082-exec-2] log: Exception Processing ErrorPage[errorCode=0, location=/error] java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.getHttpServletMapping()Ljavax/servlet/http/HttpServletMapping; at org.apache.catalina.core.ApplicationHttpRequest.setRequest(ApplicationHttpRequest.java:681) at org.apache.catalina.core.ApplicationHttpRequest.<init>(ApplicationHttpRequest.java:103) at org.apache.catalina.core.ApplicationDispatcher.wrapRequest(ApplicationDispatcher.java:857) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:513) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:480) at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:355) at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:237) at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:320) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:237) at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:242) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:834)
当前build.gradle配置:
ext { springVersion = '2.7.15' } implementation "org.springframework.boot:spring-boot-starter-web:$springVersion" implementation "org.springframework.boot:spring-boot-starter-webflux:$springVersion"
通过gradle dependencies命令分析到,spring-boot-starter-web默认依赖spring-boot-starter-tomcat:2.7.15(对应内嵌Tomcat 9.0.79):
+--- org.springframework.boot:spring-boot-starter-web:2.7.15 | +--- org.springframework.boot:spring-boot-starter:2.7.15 (*) | +--- org.springframework.boot:spring-boot-starter-json:2.7.15 (*) | +--- org.springframework.boot:spring-boot-starter-tomcat:2.7.15 | | +--- jakarta.annotation:jakarta.annotation-api:1.3.5 | | +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.79 | | +--- org.apache.tomcat.embed:tomcat-embed-el:9.0.79 | | \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.79 | | \--- org.apache.tomcat.embed:tomcat-embed-core:9.0.79 | +--- org.springframework:spring-web:5.3.29 (*)
提问:在Gradle中如何排除该默认Tomcat依赖,再引入指定版本的Tomcat(例如spring-boot-starter-tomcat:3.1.3)?
解决方案
1. 排除默认Tomcat依赖
在build.gradle中,给spring-boot-starter-web添加依赖排除规则,去掉默认的spring-boot-starter-tomcat:
implementation("org.springframework.boot:spring-boot-starter-web:$springVersion") { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' }
2. 引入指定版本的Tomcat依赖
直接添加指定版本的spring-boot-starter-tomcat依赖即可:
implementation "org.springframework.boot:spring-boot-starter-tomcat:3.1.3"
完整修改后的build.gradle示例
ext { springVersion = '2.7.15' tomcatStarterVersion = '3.1.3' } // 排除web依赖中的默认Tomcat implementation("org.springframework.boot:spring-boot-starter-web:$springVersion") { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' } implementation "org.springframework.boot:spring-boot-starter-webflux:$springVersion" // 引入指定版本的Tomcat starter implementation "org.springframework.boot:spring-boot-starter-tomcat:$tomcatStarterVersion"
注意事项
- Spring Boot 2.7.x基于Java EE(
javax.servlet),而spring-boot-starter-tomcat:3.1.3对应Tomcat 10.1.x,基于Jakarta EE(jakarta.servlet),两者API包名不兼容,直接替换会引发更多依赖冲突。 - 如果你需要升级Tomcat,建议优先选择Spring Boot 2.7.x兼容的Tomcat版本(如Tomcat 9.x的更高版本),或者直接升级Spring Boot到3.x版本(原生支持Jakarta EE)。
内容的提问来源于stack exchange,提问作者rupweb
相关产品推荐
相关产品推荐

