Tomcat启动报ClassNotFoundException是因10+版将javax.servlet改为jakarta.servlet,解决方法是降级用Tomcat 9.0.90或全面替换包名和web.xml声明。

Tomcat 启动报 java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
这是新手最常遇到的错误,本质是 Tomcat 10+ 和老教程不兼容。Servlet API 包名在 Tomcat 10 中从 javax.servlet 改成了 jakarta.servlet,但很多入门教程仍基于 Tomcat 9 或更早版本。
解决方法不是改代码,而是选对版本:
- 如果你用的是 IntelliJ IDEA 或 Eclipse 新建的 “Dynamic Web Project”,默认可能拉取 Tomcat 10+,直接换成
Tomcat 9.0.90(最新稳定 9.x)最省事 - 确认
WEB-INF/lib/下没有手动塞进javax.servlet-api.jar—— Tomcat 自带 servlet API,重复引入反而冲突 - 如果坚持用 Tomcat 10+,所有 import 必须改成
jakarta.servlet.*,且web.xml的 schema 声明也要换:<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">
web.xml 里 <servlet-mapping> 不生效,访问路径 404
根本原因通常是路径匹配规则没吃透,不是配置漏了,而是写法和请求 URL 对不上。
重点看三处是否一致:
立即学习“Java免费学习笔记(深入)”;
-
<servlet-name>在<servlet>和<servlet-mapping>两个地方必须**完全相同**(区分大小写) -
<url-pattern>是相对于应用上下文根(context path)的,比如应用部署为/myapp,而 pattern 写的是/hello,那真实访问地址是http://localhost:8080/myapp/hello,不是/myapp/servlet/hello - 不要加多余斜杠:写成
/hello/和/hello是两个不同模式;前者只匹配/hello/(结尾有斜杠),后者才匹配/hello
示例(Tomcat 9 兼容写法):
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.example.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
IDEA 里点 Run 没反应,或者提示 Artifact not configured
这不是代码问题,是项目打包结构没对上 Tomcat 的预期。IDEA 默认不会自动把 web.xml 和 class 文件组织成标准 WAR 结构。
必须手动检查两件事:
- 确保
src/main/webapp/WEB-INF/web.xml存在(Maven 标准布局),而不是放在src/或web/下随便一个文件夹里 - 打开
Project Structure → Artifacts,点击 + 号 →Web Application: Archive→ 选中你的模块;再点 + 号 →Web Application: Exploded,同样选中模块;最后确认 Output Directory 指向out/artifacts/xxx_war_exploded - 运行配置里,
Deployment选项卡下要勾选你刚建的那个 exploded artifact,并设置 Application context(比如/myapp)
Servlet 类编译后找不到 doGet() 方法,控制台报 HTTP Status 405 – Method Not Allowed
这个错说明请求确实路由到了 Servlet,但该 Servlet 没重写对应 HTTP 方法的处理函数。
常见陷阱:
- 写了
get()或handleGet()这种自定义方法 —— 必须是doGet(HttpServletRequest, HttpServletResponse),签名一个字母都不能错 - 继承了
GenericServlet却想用doGet—— 它没这个方法,得继承HttpServlet - 参数类型写错:比如把
HttpServletResponse写成ServletResponse,Java 会当成另一个重载方法,HttpServlet父类的doGet依然没被覆盖
最小可用骨架:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("Hello from Servlet!");
}
}
Servlet 的映射逻辑、类加载路径、IDE 打包行为,这三块叠在一起最容易出无声故障。很多人卡住不是因为不会写代码,而是没意识到 Tomcat 查找 class 和匹配 URL 的过程是分阶段、有优先级的 —— 比如先看 web.xml,再查注解,再 fallback 到默认 servlet,每一步断在哪,表现出来的现象都不同。










