【问题标题】:Error while having multiple servlet-api libs on classpath在类路径上有多个 servlet-api 库时出错
【发布时间】:2020-10-14 16:14:37
【问题描述】:

我有运行嵌入式 Tomcat 的 Spring Boot 应用程序。我在 docker 容器中运行它。当我尝试遵循 page 关于容器中的弹簧启动并构建分层映像时,我在尝试启动容器时收到下面提到的错误。我知道最好的方法是从我的依赖项中排除旧版本的 servlet-api,但这是不可能的,因为在我这样做时这种依赖项停止工作。不幸的是,我也无法摆脱这种依赖。有没有办法强制 Spring Boot 使用类路径中的特定实现?我已经尝试过 Jetty 和 Undertow 并且 docker 成功启动,但是使用旧版本的 lib 无法正常工作。

另一个问题是为什么当我复制 jar 并启动它时它会起作用?

我正在尝试构建的 Dockerfile:

FROM adoptopenjdk:11-jre-hotspot

ARG DEPENDENCY=target/dep 
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib 
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.test.App"]

有效的 Dockerfile:

FROM adoptopenjdk:11-jre-hotspot

COPY /target/application.jar /app/application.jar
COPY /target/lib /app/lib

ENTRYPOINT ["java", "-jar", "app/application.jar"]

需要在 pom.xml 中添加额外的插件才能实现:

                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>build-info</id>
                                <goals>
                                    <goal>build-info</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <version>3.1.0</version>
                        <configuration>
                            <finalName>ttom-osm-converter</finalName>
                            <archive>
                                <manifest>
                                    <addClasspath>true</addClasspath>
                                    <classpathPrefix>lib/</classpathPrefix>
                                    <mainClass>com.tomtom.mep.App</mainClass>
                                </manifest>
                            </archive>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-dependency-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>copy-dependencies</id>
                                <phase>prepare-package</phase>
                                <goals>
                                    <goal>copy-dependencies</goal>
                                </goals>
                                <configuration>
                                    <outputDirectory>${project.build.directory}/lib</outputDirectory>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

来自 POM 的依赖项:

        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>  <!-- lib contains servlet-api-2.5 -->
        <groupId>com.test.lib</groupId> 
        <artifactId>client</artifactId>
        <version>${model.client.version}</version>
    </dependency>

错误:

java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [NonLoginAuthenticator[StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[/path]

***************************
APPLICATION FAILED TO START
***************************

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

org.apache.catalina.authenticator.AuthenticatorBase.startInternal(AuthenticatorBase.java:1220)

The following method did not exist:

'java.lang.String javax.servlet.ServletContext.getVirtualServerName()'

The method's class, javax.servlet.ServletContext, is available from the following locations:

jar:file:/app/lib/servlet-api-2.5.jar!/javax/servlet/ServletContext.class
jar:file:/app/lib/javax.servlet-api-4.0.1.jar!/javax/servlet/ServletContext.class
jar:file:/app/lib/tomcat-embed-core-9.0.29.jar!/javax/servlet/ServletContext.class

  It was loaded from the following location:

file:/app/lib/servlet-api-2.5.jar


Action:

Correct the classpath of your application so that it contains a single, compatible version of javax.servlet.ServletContext

【问题讨论】:

  • 提供 pom 的依赖部分,尤其是 servlet api:它应该提供范围,就像 $CATALINA_HOME/lib 中的任何其他库一样。
  • 已编辑问题,我添加了依赖部分

标签: java spring-boot docker tomcat servletcontextlistener


【解决方案1】:

从关于'java.lang.String javax.servlet.ServletContext.getVirtualServerName()'的错误中,我们可以看到是在Servlet 3.1中添加的:你应该排除servlet-api:2.5。

使用以下命令:

mvn dependency:tree -Dincludes='*:servlet-api' 

这将列出所有模块/依赖项,包括 servlet-api

然后从依赖项中排除坏版本:com.test.lib 客户端甚至不应该首先包含它,这意味着也应该在其中提供依赖项。

<dependency>  <!-- lib contains servlet-api-2.5 -->
  <groupId>com.test.lib</groupId> 
  <artifactId>client</artifactId>
  <version>${model.client.version}</version>
  <exclusions> <exclusion> <groupId>...</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions>
</dependency>

请注意servlet-api groupId 随着时间的推移发生了变化:这可能是 Maven 不选择“好”servlet-api 的原因之一。

我建议你使用 maven-enforcer-plugin 来锁定这些不良依赖关系:

          <rules>
            <bannedDependencies>
              <excludes>
                <exclude>*:servlet-api:2.5</exclude>
              </excludes>
            </bannedDependencies>
          </rules>
          <fail>true</fail>

更多信息请参见http://maven.apache.org/enforcer/enforcer-rules/bannedDependencies.html

现在你提到你的 lib (com.test.lib) 似乎在没有 servlet-api 2.5 的情况下无法工作,这意味着它使用的代码可能在 Servlet 2.5 和 3.1 之间被删除:你唯一的做法是升级你的 lib至于不依赖于所述代码:

  • 第一个 servlet-api (2.5) 已加载
  • Tomcat 和 Spring Boot 是针对更高版本编译的
  • Tomcat/Spring Boot 可能会尝试使用更高版本中添加的方法
  • 您将遇到另一个错误等等。

【讨论】:

  • 为什么servlet-api 的排除项中有...?您不应该定位javax.servlet 组吗?
  • 因为 servlet 通常不能很好地服务:您应该检查父依赖项,哪个 groupId 用于您不应该获得的 servlet。您也可以尝试使用“”,这可能会奏效(我确实记得看到“”未处理的 Maven 问题)。
猜你喜欢
  • 2016-08-21
  • 2021-07-03
  • 1970-01-01
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 2021-02-11
相关资源
最近更新 更多