在运行 Spring MVC 应用程序时在 Spring Boot 中获取 NoSuchMethodError: javax.servlet.ServletContext.addServlet

Posted

技术标签:

【中文标题】在运行 Spring MVC 应用程序时在 Spring Boot 中获取 NoSuchMethodError: javax.servlet.ServletContext.addServlet【英文标题】:Getting NoSuchMethodError: javax.servlet.ServletContext.addServlet in Spring Boot while running a Spring MVC application 【发布时间】:2014-07-27 15:32:47 【问题描述】:

当我尝试使用 Spring Boot 运行 Spring MVC 应用程序时遇到异常...

ContainerBase: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:799)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    ... 6 more
Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.addServlet(Ljava/lang/String;Ljavax/servlet/Servlet;)Ljavax/servlet/ServletRegistration$Dynamic;
    at org.springframework.boot.context.embedded.ServletRegistrationBean.onStartup(ServletRegistrationBean.java:166)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:214)
    at org.springframework.boot.context.embedded.tomcat.ServletContextInitializerLifecycleListener.lifecycleEvent(ServletContextInitializerLifecycleListener.java:54)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5355)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 6 more

【问题讨论】:

你能发一下代码sn-p吗? 你使用什么依赖? 检查您的 Web 服务器支持的 servlet 版本,以及您在应用程序中实现的 servlet 版本(可以在 web.xml 中找到)。方法 addServlet() 仅在 servlet 3.0 中受支持。 看起来您的类路径上有旧版本的 tomcat 或 servlet API。使用其中一种可用工具来查看您的类路径(mvn 依赖项:树、gradle 依赖项等)。 是的,我可以在我的类路径中看到 servlet Api 2.5,但我没有在我的 pom 中提到任何这样的依赖项。那么这怎么会出现在我的类路径中,如何确保 servlet api我的项目中使用的是3.0 【参考方案1】:

我解决了它,不包括传递 servlet-api 依赖项。

在我的例子中,它是 com.github.isrsal:spring-mvc-logger

<dependency>
    <groupId>com.github.isrsal</groupId>
    <artifactId>spring-mvc-logger</artifactId>
    <version>0.2</version>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

【讨论】:

我遇到了同样的问题,对我来说 servlet 2.3 依赖来自 telnetd-x:2.1.1 作为 spring-boot-starter-web artifact-id 的一部分。 谢谢!我有同样的问题,只是它发生在我正在生产的罐子里。所以,我更喜欢从我的工件附带的依赖项中排除一次javax.servlet,而不是手动将它从依赖它的所有其他模块中排除。你有没有找到解决办法? @kumetix 在 Maven 中有一个技巧:使用版本字符串 0.99-do-not-include 创建您自己的工件 javax.serlet:servlet-api。并在 中定义这个确切的版本 我通过从 google-cloud 依赖项中排除 javax.servlet 依赖项来解决它【参考方案2】:

如果你想知道类是从哪里加载的,试试

java -verbose:class -jar foo.jar | grep javax.servlet.ServletContext

其中foo.jar 是 Gradle 或 Maven 生成的胖 JAR。例如,ServletContext 类可能是从 JDK 扩展目录中较旧的 servlet-api JAR 中读取的,而不是您的 Maven 或 Gradle 依赖项。

命令的输出看起来像这样......

$ java -verbose:class -jar build/libs/foo-0.2.3.jar | grep javax.servlet.ServletContext
[Loaded javax.servlet.ServletContext from jar:file:.../build/libs/foo-0.2.3.jar!/lib/javax.servlet-api-3.1.0.jar!/]
[Loaded javax.servlet.ServletContextListener from jar:file:.../build/libs/foo-0.2.3.jar!/lib/javax.servlet-api-3.1.0.jar!/]
[Loaded javax.servlet.ServletContextAttributeListener from jar:file:.../build/libs/foo-0.2.3.jar!/lib/javax.servlet-api-3.1.0.jar!/]

【讨论】:

我更喜欢这个答案,因为它能让你找到问题的根源。所有其他的都是“我也有这个,我通过......解决了它”。因此,如果您由于不同的原因而遇到相同的问题,则无法使用其他答案进行故障排除。这是最有用的全方位答案。 mvn dependency:tree | grep servlet-api【参考方案3】:

gradle 解决方案。

我在我的 lib jar 中遇到了类似的问题,由于某种原因,它带来了一个旧版本的 javax.servlet.ServletContext,它后来由我的 spring-boot 模块而不是它自己提供的类加载,从而导致 NoSuchMethodError

我通过编辑我的 lib 模块的 build.gradle 来修复它:

configurations 
    provided.all*.exclude group: 'javax.servlet'

【讨论】:

【参考方案4】:

我使用的是 Gradle,什么对我有用:

configurations 
    all*.exclude group: '', module: 'servlet-api'

它以所需的方式修剪依赖树。

【讨论】:

谢谢,它帮助解决了我的问题。最后我搜索了使用gradle dependencies 引入'servlet-api'的依赖项,然后我使用了标准的gradle排除方法【参考方案5】:

我在 spring-boot webapp 中有这个,只在部署服务器中(在我的本地机器上运行良好)。我通过添加来解决它:

<dependencies>
<!-- … -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
<!-- … -->

http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html

【讨论】:

【参考方案6】:

对于无法通过排除 servlet-api 来解决问题的其他人,这里有一个替代方案:

事实证明 Spring Boot 默认使用 Tomcat 8。如果您正在运行不同版本的 Tomcat 并希望修复此错误,只需将您的 tomcat 版本添加到 pom 属性:

<properties>
    <tomcat.version>7.0.63</tomcat.version>
</properties> 

【讨论】:

在 STS 3.7.2 上尝试运行 Spring 安全和 Angular 教程 (spring.io/guides/tutorials/spring-security-and-angular-js) 中的基本项目 (github.com/spring-guides/tut-spring-security-and-angular-js/…) 后,这是唯一对我有用的解决方案.使用 mvn spring-boot:run 运行良好,但不能通过 IDE 使用 tomcat 8。【参考方案7】:

为了快速解决问题,我已经从 lib 中手动删除了 servlet-api.jar,然后构建了应用程序并且它可以工作。不过,理想情况下,正如 Kumetix 所建议的那样,应该仔细检查导致它加载的依赖项在类路径中。

【讨论】:

【参考方案8】:

mvn dependency:tree 没有在我的类路径上显示servlet-api.jar,而是右键单击项目(在 Eclipse 中)并转到 Build Path / Configure Build Path显示我的默认工作区 JRE,JDK 1.7.0_51,在其 jre/lib/ext 目录中有那个 jar(至少在我的安装中)。我尝试将其从我的类路径中删除,但未能成功。 @Pedro 的 solution 将 tomcat 版本强制为 7 有效。安装最新版本的 Java 7:JDK 1.7.0_79 也是如此。

【讨论】:

【参考方案9】:

测试(使用 gradle + spock)期间尝试启动我的 Spring Boot 服务器时遇到了这个问题。我将问题追溯到wiremock库。

这解决了它:

testCompile('com.github.tomakehurst:wiremock:1.58') 
    exclude module: 'servlet-api' // this exclude fixed it


仅供参考

gradle dependencies

显示(删节):

\--- com.github.tomakehurst:wiremock:1.58
     +--- org.mortbay.jetty:jetty:6.1.26
          +--- org.mortbay.jetty:jetty-util:6.1.26
          \--- org.mortbay.jetty:servlet-api:2.5-20081211

古老的 (2008) org.mortbay.jetty:servlet-api jar 包含与 Spring Boot 1.3.2 版本不兼容的 ServletContext 版本(至少在 1.2.6 之前可以正常工作)。

【讨论】:

【参考方案10】:

如果您使用 STS 并且已将 Groovy 库添加到您的项目(项目属性 -> Java 构建路径 -> 库),请确保选择“否,仅包含 groovy-all”选项。 “Yes, include groovy-all and bsf.jar ..., servlet-2.4.jar”的另一个选项添加了 servlet-2.4.jar,它与嵌入式 tomcat8 类冲突,导致此问题。

【讨论】:

这也是发生在我身上的事!【参考方案11】:

我也遇到了这个错误。我有一个 Maven/Spring MVC/Spring Boot 项目。我使用 IntelliJ 作为 IDE,每当我向 POM.xml 添加新的依赖项时,IDE 都会更改 .iml 文件(如预期的那样),但奇怪的是它将以下行移至文件顶部:

<orderEntry type="library" name="Java EE 6-Java EE 6" level="project" />

一旦发生这种情况,我的项目将无法编译,并且出现同样的错误:

java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;

只需将 Java EE 的 orderEntry 移回底部,错误就会消失,我可以再次编译。

【讨论】:

【参考方案12】:

我在我的项目中使用了 Jetty,但遇到了同样的错误。我的快速解决方案是从 Maven 依赖项中排除嵌入式 Tomcat:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-ws</artifactId>
    <version>1.2.0.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

【讨论】:

【参考方案13】:

根据这个解决方案: https://stevewall123.wordpress.com/2015/04/17/spring-boot-application-and-tomcat-error-a-child-container-failed-during-start/

您可以通过设置“tomcat.version”属性来更改Tomcat版本:

ext['tomcat.version'] = '7.0.63' //my version of Tomcat

dependencies 
    compile 'org.springframework.boot:spring-boot-starter-web'

感谢那篇文章的作者,它对我有用。

干杯:)

【讨论】:

【参考方案14】:

我使用 Spring-boot 运行 Hadoop 2.7.2,以下 Hadoop 依赖项使用 javax.servlet,这会阻止嵌入式 tomcat 版本启动。我的 POM 中的以下排除项解决了问题

                                    <dependency>
                                    <groupId>org.apache.hadoop</groupId>
                                    <artifactId>hadoop-hdfs</artifactId>
                                    <version>2.7.2</version>
                                    <exclusions>
                                        <exclusion>
                                            <artifactId>servlet-api</artifactId>
                                            <groupId>javax.servlet</groupId>
                                        </exclusion>
                                    </exclusions>
                                </dependency>
                                <dependency>
                                    <groupId>org.springframework.data</groupId>
                                    <artifactId>spring-data-hadoop-boot</artifactId>
                                    <version>2.3.0.RELEASE-hadoop26</version>
                                    <exclusions>
                                        <exclusion>
                                            <artifactId>servlet-api</artifactId>
                                            <groupId>javax.servlet</groupId>
                                        </exclusion>
                                    </exclusions>
                                </dependency>

                                <dependency>
                                    <groupId>org.apache.hadoop</groupId>
                                    <artifactId>hadoop-common</artifactId>
                                    <version>2.7.2</version>
                                    <exclusions>
                                        <exclusion>
                                            <artifactId>slf4j-log4j12</artifactId>
                                            <groupId>org.slf4j</groupId>
                                        </exclusion>
                                        <exclusion>
                                            <artifactId>servlet-api</artifactId>
                                            <groupId>javax.servlet</groupId>
                                        </exclusion>
                                    </exclusions>
                                </dependency>

【讨论】:

【参考方案15】:

除了知道它以某种方式进入我的类路径之外,我无法找到有问题的参考,所以这是我为使用 eclipse 的懒惰开发人员提供的 2 美分:

向下滚动项目树下的 maven 依赖项列表(通常在 Java 资源 -> 库 -> Maven 依赖项下),跟踪您希望从打包的 JAR 中排除的有问题的添加 jar,右键单击它 -> 选择 Maven -> 排除 Maven 工件! 瞧——会自动将排除项添加到您的 pom 中,就在引用它的依赖项下。

我的 BTW 是 jcifs...

        <dependency>
        <groupId>org.codelibs</groupId>
        <artifactId>jcifs</artifactId>
        <version>1.3.18.2</version>
        <exclusions>
            <exclusion>
                <artifactId>servlet-api</artifactId>
                <groupId>javax.servlet</groupId>
            </exclusion>
        </exclusions>
    </dependency>

祝你好运!

【讨论】:

以上是关于在运行 Spring MVC 应用程序时在 Spring Boot 中获取 NoSuchMethodError: javax.servlet.ServletContext.addServlet的主要内容,如果未能解决你的问题,请参考以下文章

使用spring mvc在按钮单击时在表上添加行并将添加的行绑定到modelAttribute

使用 STOMP/WebSockets/Spring 从 @SubscribeMapping 返回原始字符串

如何在spring mvc 3.0中的同一jsp中显示运行时错误消息或sql错误消息

使用maven编译并运行Spring项目

春天云网关;在类路径上发现 Spring MVC,与 Spring Cloud Gateway 不兼容问题

spring MVC配置详解