如何在Maven的pom.xml中正确配置Jakarta EE库以适用于Tomcat?

15

我一直在努力实现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 实际上实现了哪个类。

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>  
我曾希望在部署描述符中使用正确的Jakarta EE XML模式会导致使用正确的类,但显然没有。WAR文件似乎包含了正确的JAR包...
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生成的项目,我已经手动修改过了。使用maven和eclipse构建时,在部署war文件时会产生相同的结果。

1个回答

39

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

Tomcat 9.x

对于基于Servlet 4.0、JSP 2.3、EL 3.0、WS 1.1和JASIC 1.0的Tomcat 9.x,您应该使用javax.*导入和<dependencies>部分整个地最小化如下:

<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>

Tomcat 10.0.x

Tomcat 10.0.x基于Servlet 5.0、JSP 3.0、EL 4.0、WS 2.0和JASIC 2.0。您应该使用jakarta.*导入,并且<dependencies>部分全部如下所示:

<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>

Tomcat 10.1.x

针对基于Servlet 6.0、JSP 3.1、EL 5.0、WS 2.1和JASIC 3.0的Tomcat 10.1.x版本,建议您使用 jakarta.* 导入,并将整个 <dependencies> 部分 至少 设置为:

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

无论如何

请注意,对于它们,<scope> 明确设置为 provided。这意味着 Maven 不应将物理 JAR 文件包含在生成的 WAR 文件的 /WEB-INF/lib 中(因为 Tomcat 自己已经提供了它们!)。否则,您最终会在运行时类路径中遇到由重复的类引起的运行时冲突。

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

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

但是,如果您在为 Tomcat 开发代码时非常清楚这个限制(即确保自己不会意外使用例如 JSTL、CDI、BV、JPA 等等而实际上没有在 Tomcat 上安装它们),并且仅想最小化 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.0.x:

<dependencies>
    <dependency>
        <groupId>jakarta.platform</groupId>
        <artifactId>jakarta.jakartaee-web-api</artifactId>
        <version>9.0.0</version> <!-- Use 9.1.0 if you're already on Java 11 -->
        <scope>provided</scope>
    </dependency>
</dependencies>

对于Tomcat 10.1.x版本,可以使用以下内容:

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

另请参阅:


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