运行Spring MVC应用程序时,在Spring Boot中遇到NoSuchMethodError: javax.servlet.ServletContext.addServlet错误

40

当我尝试使用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

请问您能否提供一段代码片段吗? - user3145373 ツ
你正在使用哪些依赖项? - geoand
4
请检查您的Web服务器支持的Servlet版本以及您在应用程序中实现的Servlet版本(可以在web.xml中找到)。方法addServlet()仅在Servlet 3.0中受支持。 - Wundwin Born
2
看起来你的Tomcat版本或Servlet API版本较旧,已经在类路径上。使用可用的工具之一查看你的类路径(mvn dependency:tree、gradle dependencies等)。 - Dave Syer
是的,我可以在我的类路径中看到Servlet Api 2.5,但我没有在我的pom中提及任何这样的依赖关系。那么这是怎么出现在我的类路径中的?我该怎么做才能确保在我的项目中使用Servlet Api 3.0? - Arghya Sadhu
@Dave Syer.....我现在在我的类路径中有javax.servlet-api-3.1.0.jar..但是我仍然得到上面的异常。 - Arghya Sadhu
16个回答

31

如果你想找出类从哪里加载,可以尝试使用

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

其中foo.jar是由Gradle或Maven生成的fat 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!/]

4
我更喜欢这个答案,因为它可以让你找到问题的根源。其他答案都是“我也遇到过这个问题,并通过……解决了它。”如果由于不同的原因而导致相同的问题,则无法使用其他答案进行排除故障。这是最好的通用有用答案。 - ETL
mvn dependency:tree | grep servlet-api - kinjelom

27

我通过排除一个传递性的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 的一部分。 - randominstanceOfLivingThing
谢谢!我有同样的问题,只不过它发生在我正在制作的一个jar包中。因此,我更喜欢从我的构件中排除javax.servlet,而不是手动从所有依赖它的其他模块中排除它。你有没有找到这样做的方法? - kumetix
@kumetix 有一个在Maven中实现这个的hack方法:创建你自己的artifactjavax.serlet:servlet-api,版本字符串为0.99-do-not-include。并在<dependency-management>中定义这个完全相同的版本。 - Grogi
1
我通过从google-cloud依赖中排除javax.servlet依赖项来解决了这个问题。 - Halil

23

Gradle 解决方案。

我在我的 lib jar 中遇到了类似的问题,其中某些原因导致旧版本的 javax.servlet.ServletContext 被带入,后来被我的 spring-boot 模块加载而不是其自己提供的类,从而引起 NoSuchMethodError。

我通过编辑我的 lib 模块的 build.gradle 文件来解决它:

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

9

对于那些无法通过排除 servlet-api 解决问题的人,这里提供另一种解决方案:

原来Spring Boot默认使用Tomcat 8。如果您正在运行不同版本的Tomcat并希望解决此错误,只需将您的Tomcat版本添加到pom属性中即可:

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

1
经过数小时的尝试,我终于在STS 3.7.2上成功运行了Spring安全性和Angular教程(https://spring.io/guides/tutorials/spring-security-and-angular-js/)中的基本项目(https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/basic)。这是唯一对我有效的解决方案。它可以通过mvn spring-boot:run正常运行,但无法通过带有Tomcat 8的IDE运行。 - whistling_marmot

8

7

在进行测试时(使用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版本中可以正常工作)。


6

我曾使用Gradle,以下是适用于我的方法:

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

它按照预期的方式简化了依赖树。

谢谢,它帮助我解决了我的问题。最后我使用了gradle dependencies查找引入了servlet-api的依赖项,然后使用标准的gradle排除方法。 - Michal Moravcik

6

我使用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>

4

4
我无法追踪到有问题的引用,但我知道它以某种方式进入了我的类路径,因此对于使用Eclipse的懒惰开发人员,这是我的建议:下滑项目树下面的Maven依赖项列表(通常位于Java Resources -> Libraries -> Maven Dependencies),找到你希望从打包的JAR中排除的有问题的添加了的jar包,右键单击它-> 选择Maven-> 排除Maven Artifact!瞧 - 在引用它的依赖项正下方将自动添加一个排除内容到你的POM文件。顺带一提,我的是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>

祝好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接