【问题标题】:Tomcat casting servlets to javax.servlet.Servlet instead of jakarta.servlet.http.HttpServletTomcat 将 servlet 转换为 javax.servlet.Servlet 而不是 jakarta.servlet.http.HttpServlet
【发布时间】:2021-04-18 13:53:12
【问题描述】:

我一直在徒劳地尝试实现 tomcat 9 的 jakarta servlet,而不是之前的 javax.servlet 实现(据我了解,jakarta 包是前进的方向)。问题是当我将浏览器指向我的 servlet 的 url 时,我收到以下错误...

java.lang.ClassCastException: class cti.nm.web.Index cannot be cast to class javax.servlet.Servlet (cti.nm.web.Index is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @48c76607; javax.servlet.Servlet is in unnamed module of loader java.net.URLClassLoader @621be5d1)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:432)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:832)

问题很明显。 Tomcat 试图将我的 jakarta.servlet.http.HttpServlet 转换为 javax.servlet.Servlet,这显然是行不通的。我想不通的是如何告诉它 servlet 实际实现的是什么类。

类本身被声明为 ...

package cti.nm.web;
import jakarta.servlet.http.HttpServlet;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class Index extends HttpServlet {
     
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
      throws IOException, ServletException {

        response.setContentType("text/html");
       //print a bunch of stuff
    }
}

我的web.xml文件如下...

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">
         
    <display-name>NMWeb</display-name>
    <description>
       NMWeb Description
    </description>

    <servlet>
        <servlet-name>Index</servlet-name>
        <servlet-class>cti.nm.web.Index</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Index</servlet-name>
        <url-pattern>/NMWeb</url-pattern>
    </servlet-mapping>

</web-app>  

我曾希望在部署描述符中使用正确的 jakartaee xml 模式会导致使用正确的类,但显然不是。 WAR 似乎包含正确的罐子......

jakarta.jakartaee-api-9.0.0.jar
tomcat-el-api-10.0.0.jar
tomcat-servlet-api-10.0.0.jar jakarta.servlet-api-5.0.0.jar
tomcat-jsp-api-10.0.0.jar

pom 被指定为 ...

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cti.nm.NMWeb</groupId>
  <artifactId>NMWeb</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <build>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>[9.0.0,)</version>
      
    </dependency>
    <!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>[5.0.0,)</version>
      
    </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jsp-api -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jsp-api</artifactId>
            <version>[10.0.0,)</version>
       
        </dependency>
  </dependencies>
</project>

在网上搜索这个问题的答案已经有好几天了,我似乎找不到这种确切情况的任何其他实例。我应该提一下,这是我手动修改的 Eclipse 生成的 probject。当我部署 war 文件时,使用 maven 和 eclipse 构建会产生相同的结果。

【问题讨论】:

    标签: maven tomcat servlets classcastexception


    【解决方案1】:

    您基本上是在 WAR 中物理包含 Tomcat 10.x 特定库,然后将 WAR 部署到 Tomcat 9.x。这根本不是正确的方法。此外,Tomcat 10.x 是第一个被 Jakartified 的版本,而不是 Tomcat 9.x。

    对于基于 Servlet 4.0、JSP 2.3、EL 3.0、WS 1.1 和 JASIC 1.0 的 Tomcat 9.x,您应该使用 javax.* 导入,整个 &lt;dependencies&gt; 部分应该至少看起来像:

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.security.enterprise</groupId>
            <artifactId>javax.security.enterprise-api</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

    对于基于 Servlet 5.0、JSP 3.0、EL 4.0、WS 2.0 和 JASIC 2.0 的 Tomcat 10.x,您应该使用 jakarta.* 导入并且 整个 &lt;dependencies&gt; 部分应该至少看起来像:

    <dependencies>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet.jsp</groupId>
            <artifactId>jakarta.servlet.jsp-api</artifactId>
            <version>3.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.el</groupId>
            <artifactId>jakarta.el-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.websocket</groupId>
            <artifactId>jakarta.websocket-api</artifactId>
            <version>2.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.security.enterprise</groupId>
            <artifactId>jakarta.security.enterprise-api</artifactId>
            <version>2.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

    请注意,&lt;scope&gt; 明确设置为 provided,这意味着 Maven 应该在生成的 WAR 文件的 /WEB-INF/lib 中包含物理 JAR 文件(因为它是Tomcat本身已经提供了!)。否则,您只会因运行时类路径中的重复类而导致运行时冲突。

    还请注意,Tomcat 不是 JEE 服务器,因此根据定义导入 Tomcat 9.x 的 javax:javaee-api 或 Tomcat 10.x 的 jakarta.platform:jakarta.jakartaee-api 是错误的。因为它将允许您针对其他 JEE 组件(例如 JSF、JSTL、CDI、BV、EJB、JPA、JAX-RS、JSONB 等)编译您的代码,而 Tomcat 实际上并没有提供它们。 Tomcat only offers Servlet, JSP, EL, WS and JASIC out the box,所以你应该只在pom.xml中声明它们。

    比如JSTL需要单独安装How to install JSTL? The absolute uri: http://java.sun.com/jstl/core cannot be resolved,JSF需要单独安装How to properly install and configure JSF libraries via Maven?,CDI需要单独安装How to install and use CDI on Tomcat?

    如果您在为 Tomcat 开发代码期间非常清楚这一限制(即,请确保您自己不会在未先在 Tomcat 中实际安装它们的情况下意外使用例如 JSTL、CDI、BV、JPA 等),并且只想最小化pom.xml 样板,那么您也可以摆脱Tomcat 9.x 的这种极简依赖配置:

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

    或者对于 Tomcat 10.x:

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>9.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

    另见:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-05
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 2011-11-26
      • 2017-07-07
      相关资源
      最近更新 更多