maven - Tomcat 将 servlet 转换为 javax.servlet.Servlet 而不是 jakarta.servlet.http.HttpServlet
问题描述
我一直在徒劳地尝试实现 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 构建会产生相同的结果。
解决方案
您基本上是在 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.*
,并且整个 <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>
对于基于 Servlet 5.0、JSP 3.0、EL 4.0、WS 2.0 和 JASIC 2.0 的 Tomcat 10.x,您应该使用导入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>
请注意,它们<scope>
是明确设置provided
的,这意味着 Maven 不应在生成的 WAR 文件中包含物理 JAR/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
.
例如,JSTL 需要按照如何安装 JSTL 中的说明单独安装?绝对 uri:http://java.sun.com/jstl/core 无法解析,需要按照如何通过 Maven 正确安装和配置 JSF 库中的说明单独安装 JSF?并且 CDI 需要按照如何在 Tomcat 上安装和使用 CDI中的说明单独安装?
但是,如果您在为 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>
也可以看看:
推荐阅读
- c++ - 使用 (float&)int 键入双关语有效, (float const&)int 转换为 (float)int 代替?
- java - 由于有 JSoup,无法在 cmd 终端中找到或加载主类
- swift - UIAlertController 在某些情况下不会被解雇
- nginx - Nginx 未在成功验证时重定向到 url
- python - 如何将多个泡菜文件放在一个泡菜文件中
- vue.js - Vue Vuex:在计算属性更改之前,旧数据会保留一段时间
- reactjs - 没有日期和月份的年份选择器
- javascript - 多个音频地址对齐项目 HTML
- android - Firebase DataSnapshot getValue 未在初始化主体上设置属性
- oracle - 更新:功能编辑与其他设计