
如果你最近刚开始学 Java Web八成会在同一个地方卡壳课本里的 Servlet 代码都看懂了打开 IDEA 却不知道项目该怎么建、Tomcat 该怎么挂、web.xml 里那几行配置写错一个字母又要出什么幺蛾子。网上教程零散得很有人用 Eclipse 讲有人用 Maven 讲照着敲半天还是跑不起来。这篇我就用 IDEA 把“创建 Servlet 程序”这个完整链条走一遍从环境准备、新建工程、手写 Servlet 类、配置映射到挂上 Tomcat 运行、断点调试每一步都给你可以直接照做的操作顺便把我在实际教学和项目里遇到的高发问题一起交代清楚。适合刚学完 Java 基础、准备跨进 Web 开发的初学者也适合需要临时接手一个老 web 项目、但没用 IDEA 从头配过 Servlet 的人。1. 动手之前Servlet 这门课到底在学什么很多同学着急想直接跳到“怎么在 IDEA 里点按钮”。但如果你不明白 Servlet 在整条 Web 请求链路里的位置后面遇到 404、乱码、端口冲突这些破事连排查方向都没有。1.1 一个 Servlet 程序到底由哪些部分组成抛开复杂的框架概念一个能跑的 Servlet 程序其实只有三块Java 类、Servlet 容器、映射配置。Java 类你写的HttpServlet子类里面重写doGet/doPost处理具体的业务逻辑。Servlet 容器最常见的就是 Tomcat。它负责接收 HTTP 请求、找到对应的 Servlet、调用你的 Java 方法再把响应返回给浏览器。映射配置告诉容器“哪个 URL 地址对应哪个 Servlet 类”。没有这一步容器看到/hello这个请求根本不知道要找谁。你可以把 Servlet 想象成餐厅的服务员Tomcat 是餐厅本身。客人点菜相当于浏览器发了一个 HTTP 请求服务员拿着菜单去后厨下单再把做好的菜端回来——对应HttpServletRequest请求对象和HttpServletResponse响应对象。你写的 doGet / doPost就是服务员接单时的具体动作。1.2 为什么我劝你先别急着上框架现在 Spring Boot 确实方便注解一标、内置容器一起一个控制器就跑起来了。但框架把太多细节封装掉之后很多人根本没搞明白“一个 HTTP 请求从浏览器出发经过容器、Servlet、过滤器、拦截器最终再回到浏览器”这个过程到底发生了什么。我第一次带培训班时观察到一个很明显的现象先手写 Servlet 的同学后面学过滤器、拦截器、Spring MVC 的前端控制器时基本一遍就懂没写过 Servlet、直接上框架的同学遇到“为什么请求进不了这个方法”“为什么加了个过滤器就全盘 404”这种问题往往会卡非常久。所以不管你以后用不用 SpringServlet 本身真的值得亲手从零搭一遍。这篇的操作就是走这个过程从无到有。2. 环境准备JDK、Tomcat 和 IDEA 版本怎么搭才少踩坑很多人第一步就栽在版本上。不是 JDK 太新就是 Tomcat 太旧最后连 servlet-api 都导入不进去。2.1 我的版本搭配建议下面这张表是这几年来我验证过相对稳的组合不是唯一方案但照着做基本不会在环境层面浪费时间。运行环境Servlet 规范依赖里的包名适合场景Tomcat 9Servlet 4.0javax.servlet.*老教程、公司存量项目、网上的多数资料Tomcat 10Servlet 5.0jakarta.servlet.*新项目、新教材、愿意用新版本的人至于 JDKTomcat 9 搭配 JDK 8 或 JDK 11 都很稳JDK 17 以上建议直接用 Tomcat 10.1。IDEA 用 Community 版也能跑完整流程这点后面会详细说但如果你手里是 Ultimate 版向导会省很多事。2.2 JDK 安装与 JAVA_HOME 验证JDK 安装本身不复杂关键是装完以后检查环境变量。Windows 下建议在系统变量里新建JAVA_HOME值填写 JDK 的安装路径比如C:\Program Files\Java\jdk-11。然后把%JAVA_HOME%\bin加到 Path 变量里。打开命令行依次执行java -version javac -version echo %JAVA_HOME%能正常输出版本号、并且JAVA_HOME不是空路径这一步就过了。macOS 或 Linux 用户一般不需要手动配直接用export JAVA_HOME/Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home这种形式指定就行。很多新人卡在“IDEA 里明明装了 JDK命令行却找不到 java”多半就是 Path 没配好。重启一次 IDEA 很重要它不会实时刷新环境变量。2.3 Tomcat 下载与解压背后的两个细节Tomcat 建议去官网下压缩包不要用安装版。Windows 下解压到一个没有中文和空格的路径比如D:\dev\apache-tomcat-9.0.86。解压后你会看到几个目录其中只要记住这几个bin启动和关闭脚本。conf核心配置文件server.xml管端口web.xml是全局 Servlet 配置。webapps默认发布目录打好的 war 包或解压的 web 应用丢进去就能被访问。libTomcat 自带的 jar 包servlet-api.jar就在这里。这里有个容易糊涂的点很多人以为必须设置CATALINA_HOME环境变量。实际上如果你只在 IDEA 里运行 Tomcat并不强制要求IDEA 运行配置里直接指定 Tomcat 主目录即可。命令行远程部署才会用到CATALINA_HOME新手阶段不用纠结。3. 创建 Web 工程两种建项目方式与目录结构接下来进入正题在 IDEA 里把项目结构搭出来。我会同时讲 Ultimate 版本和 Community 版本两种做法因为不少学生手里是免费的 Community 版网上一搜全是 Ultimate 的截图直接懵掉。3.1 方案一Ultimate 版直接走 Java Enterprise 向导Ultimate 版新建项目时左侧选“Java Enterprise”或者“Jakarta EE”然后在面板里勾选Web Application。IDEA 会自动帮你生成 webapp 目录、web.xml 以及一个最简单的 index.jsp。具体步骤大概是这样的File → New → Project选择 Java Enterprise。旁边选择你的 JDK 版本应用服务器选 Tomcat。右侧勾选 Web Application。点击 FinishIDEA 自动创建一个带 webapp 的模块。这样做完项目里会自动出现src/main/webapp目录下面就是WEB-INF和web.xml。对初学者来说这是最省事的路径。3.2 方案二Community 版新建普通 Java 项目再手动补 web 能力Community 版没有 Java Enterprise 向导也不带内置 Tomcat 集成插件但这不代表做不了。只需按部就班“手动改装”File → New → Project选 Java选好 JDK什么都不勾先建一个空 Java 项目。在项目名上右键选择 Add Framework Support。在弹出的列表里勾选Web Application。如果版本支持IDEA 会直接生成 webapp 目录和 web.xml如果没自动生成就手动在src/main/java旁边新建src/main/webapp/WEB-INF再右键新建一个web.xml。注意Community 版 Add Framework Support 之后IDEA 顶部一般不会有“Run on Tomcat”这种按钮。解决办法是给 IDEA 装一个“Smart Tomcat”社区插件或者干脆用传统方式把项目打成 war 包丢到 Tomcat 的 webapps 目录里再手动启动 Tomcat。后面我重点演示在 IDEA 里用运行配置直接启动的方式这对开发调试最方便插件装个几分钟就好。3.3 看懂 webapp 目录结构比盲目建目录重要一万倍你创建 Web 项目后目录大致长这样项目名 ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ │ ├── WEB-INF │ │ │ └── web.xml │ │ └── index.jsp有几个关键点必须说清楚。webapp是 Web 应用的根目录。JSP、HTML、CSS、图片都放这里浏览器访问时直接对应 URL。WEB-INF是一个受保护的目录里面的文件浏览器不能直接访问。web.xml 放在里面是容器识别一个 Web 应用的标志。src/main/java放你的 Servlet 类和其他 Java 代码。为什么web.xml这么重要因为容器启动 Web 应用时会先读这个文件把里面声明的 Servlet、过滤器、监听器全部加载注册。如果你的web.xml路径放错或者文件格式不对最常见的表现就是Tomcat 启了日志也没有报错但访问一切页面都是 404。4. 编写 Servlet 类从 doGet 到 web.xml 映射项目骨架搭好了现在开始写真正干活的代码。我会用“请求一个带姓名参数的页面页面输出一段问候语”这个例子来讲简单但覆盖了大多数关键知识点。4.1 第一个 Servlet 类的完整代码在src/main/java下建立一个包比如com.demo.servlet然后创建一个类HelloServletpackage com.demo.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容的类型和编码必须先写 response.setContentType(text/html; charsetUTF-8); // 获取请求参数名字叫作 name String name request.getParameter(name); if (name null || name.trim().isEmpty()) { name 陌生人; } PrintWriter out response.getWriter(); out.println(!DOCTYPE html); out.println(html); out.println(headmeta charset\UTF-8\titleHello Servlet/title/head); out.println(body); out.println(h1你好 name !/h1); out.println(/body); out.println(/html); out.close(); } Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 暂时复用 doGet 的逻辑避免表单 POST 进来没反应 doGet(request, response); } }几个细节提醒javax.servlet.http.HttpServlet是 Tomcat 9 及以下的包名。如果你用 Tomcat 10要把第一行改成import jakarta.servlet.http.*;其他地方一样。response.setContentType一定要写在最前面否则中文大概率乱码。doGet和doPost通常至少实现一个。只重写service方法也可以但实际工作中按请求类型分开处理更清晰。拿到PrintWriter之后直接用 HTML 标签拼接输出。这段代码本身不复杂新手最容易犯的错是导错包——导成com.sun.net.httpserver.HttpServer或者javax.servlet.http.HttpServlet没找到本质都是 jar 包没导入。我放到最后一节专门讲。4.2 请求参数和响应对象别只顾着写想想为什么是它俩HttpServletRequest和HttpServletResponse是 Servlet 里最重要的两个对象。请求对象就是浏览器递给容器的一整套数据URL、请求参数、请求头、Cookie等等。你在 doGet 里写request.getParameter(name)就是从 URL 的问号后面或者表单正文里取值。比如访问http://localhost:8080/demo/hello?nameAlicename拿到的就是Alice。响应对象则是容器交给你“往浏览器回话”的通道。你设置类型、设置编码、往getWriter()里写内容最终都会变成浏览器收到的 HTTP 响应体。新手不用死记 API记住一个方向就够了请求是入口响应是出口Servlet 夹在两者中间干活。后面学 Spring MVC 的 Controller 本质上还是这套模型只是把写响应的工作替你封装好了。4.3 web.xml 里的 servlet 和 servlet-mapping到底在配什么光有 Java 类还不够容器不知道/hello这个地址应该找谁。在src/main/webapp/WEB-INF/web.xml里写这样的配置?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 servlet servlet-nameHelloServlet/servlet-name servlet-classcom.demo.servlet.HelloServlet/servlet-class /servlet servlet-mapping servlet-nameHelloServlet/servlet-name url-pattern/hello/url-pattern /servlet-mapping /web-app这里有两个标签块初学者经常混。servlet是“注册”这个 Servlet告诉容器“有个类叫com.demo.servlet.HelloServlet”。servlet-mapping是“绑定”URL告诉容器“访问/hello时去找刚才注册的那个名字”。通俗点说servlet-name就像一个员工编号两处必须能对上。写错编号或者 class 路径写错启动时通常不会报错因为类还没被真正加载只有你访问/hello时才会看到 404 或 500。另外url-pattern支持多级路径比如/api/*、*.do这种写法。新手阶段直接在/后面写简单名称就够了。4.4 用 WebServlet 注解省去配置的另一种写法从 Servlet 3.0 开始支持用注解代替 web.xml 里的映射。只改一处Java 类上加上WebServlet(/hello) public class HelloServlet extends HttpServlet { // 类里代码不变 }然后 web.xml 里那段servlet和servlet-mapping就可以删掉容器启动时会自动扫描带WebServlet注解的类。那我建议你学哪一种我个人的看法是两个都得会。注解写起来省事适合自己建的新项目但很多公司遗留项目里 web.xml 动辄几百行你不认识servlet、filter、listener这些配置接手时会非常痛苦。先亲手用 web.xml 把流程捋顺再换注解你会发现一切豁然开朗。5. 部署运行把工程挂到 Tomcat 上并调试代码写完了接下来这一步是关键中的关键怎么把它变成 Tomcat 能运行的应用。5.1 配置 Tomcat Server绑定 IDEA 与容器如果你用 Ultimate 版或者装了 Smart Tomcat 插件可以走 IDEA 的 Tomcat 运行配置。步骤是打开 Run → Edit Configurations。点左上角“”选择 Tomcat Server → Local。在 Configuration 里点击 Application server 右侧的 Configure...选择你解压的 Tomcat 主目录。HTTP port 默认 8080不需要改就先不动。切到 Deployment 标签页点“”选择 Artifact。选你的项目 war exploded。Application context 这里要注意它决定了访问前缀。如果填/demo那你访问地址就是http://localhost:8080/demo/hello如果填/访问地址直接是http://localhost:8080/hello。很多新手配完了问“为什么我访问的地址不对”十有八九是上下文路径和url-pattern没换算清楚。上下文路径在前Servlet 映射路径在后两个拼起来才是完整 URL。5.2 artifact 与 war、war exploded 到底选哪个IDEA 部署的时候会让你选 artifact 类型最常见的是war和war exploded。区别很简单war把项目打成压缩包部署到 Tomcat 后由容器解压。接近正式环境但每次修改代码都要重新打包调试很不方便。war exploded直接把解压后的目录作为 Web 应用根目录。IDEA 会把编译后的 class 和 webapp 资源实时同步过去改 JSP 刷新浏览器基本就能看见改 Java 类配合热重载也能快速生效。开发阶段我的建议是无脑选war exploded。你看到“exploded”不要觉得高级它就是“未打包、以目录形式存在”的意思。5.3 启动、访问与断点调试配置完直接点右上角的绿色三角启动 Tomcat。启动日志里看到类似这段输出说明部署成功Deployment of web application archive [xxx] has finished in [x] ms然后打开浏览器访问对应的地址。以我的配置为例上下文路径/Servlet 映射/hello访问http://localhost:8080/hello?nameAlice页面上就会显示“你好Alice!”一个 Servlet 程序就完整跑通了。调试时在doGet里打一个断点然后点 IDEA 右上角的“臭虫”图标进入 Debug 模式。浏览器再次发起请求请求会停在断点上你可以在 Variables 面板里看到request、response、name这些变量当前的值。这一步对理解请求生命周期帮助极大别只停留在“能跑就行”。6. 高发问题排查404、乱码、端口占用三个大坑最后这部分是给每个刚把 Servlet 跑起来的同学减少折磨的。我见过的问题里80% 都集中在这三个地方。6.1 一访问就 404按顺序排查这 5 处404 说明请求到了 Tomcat但容器没找到对应资源。按下面顺序查基本能定位看启动日志确定应用确实部署成功而不是启动到一半炸了。核对访问地址上下文路径 Servlet 映射路径缺一个都不行。检查 web.xml 里 class 路径写没写对。com.demo.servlet.HelloServlet和com.demo.Servlet.HelloServlet这种大小写错误非常隐蔽。检查编译后的 class 是否在WEB-INF/classes下。IDEA 一般是自动编译但如果你碰过 build 配置可能 class 没输出过去。如果用了注解确认注解路径前有/例如WebServlet(/hello)漏了斜杠等于没配。6.2 中文乱码要从请求、响应、容器三层治理乱码是最让新手崩溃的问题因为它的原因经常不止一个。我把可能出现的乱码场景和对应解法列一下乱码场景原因解法页面输出的中文变“???”响应编码没设置response.setContentType(text/html; charsetUTF-8);控制台 Tomcat 日志中文乱码Windows 下控制台默认 GBK与 UTF-8 日志冲突改conf/logging.properties把 ConsoleHandler 编码设为 GBKURL 参数传中文乱码Tomcat 8 默认 UTF-8老版本或手动改过配置除外确认 Tomcatconf/server.xml中 Connector 加了URIEncodingUTF-8请求体表单 POST 乱码请求体解码字符集不对在doPost开头写request.setCharacterEncoding(UTF-8);一个非常实用的建议先在 IDEA 里把 File Encodings 全部统一成 UTF-8再检查 Tomcat 配置文件最后检查代码里有没有设置编码。一层层查下来乱码基本能解决。6.3 端口被占用Tomcat 一启动就秒退Tomcat 默认监听 8080如果这个端口被其他程序占用启动日志会立刻报Address already in use。Windows 下用这个命令查看谁占用了 8080netstat -ano | findstr :8080拿最后一列的 PID再用任务管理器找到对应进程结束或者直接改 Tomcat 的端口。改端口时在conf/server.xml里找Connector port8080 ...改成 8081 之类同时记得把 IDEA 运行配置里的端口也改成一致否则还是对不上。Linux 或 macOS 下用lsof -i:8080找到占用进程的 PID 后kill -9 进程号即可。6.4 编译时报找不到 Servlet 相关类如果 IDEA 里写代码时HttpServlet一直标红说明项目的编译 classpath 里没有 servlet-api。解决方式有两种不用 Maven 的话File → Project Structure → Libraries → 点“”→ Java选择 Tomcat 目录下lib/servlet-api.jar。用 Maven 的话在 pom.xml 里加依赖注意 scope 设为provided因为运行时 Tomcat 已经提供了这个 jar打包时不应再打进去。dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependencyTomcat 10 的用户把上面的 groupId 和 artifactId 换成jakarta.servlet、jakarta.servlet-api就行。scope这个东西新手容易忽略。写成provided的意思是“我编译时需要它但运行时你不用帮我带着”。如果写成compile又不小心把 war 包带上和 Tomcat 自带的 servlet-api 冲突会出现奇怪的类加载错误。最后分享一个我实际带项目时的小习惯第一次跑通 Servlet 后别急着往下学框架先自己加一个登录页、一个表单提交、一个请求转发体会一下“浏览器 → 容器 → Servlet → 响应”整个链路。等你哪天发现 web.xml 配置越来越繁琐、想偷懒了再回头学框架这时候你会特别理解那些注解到底帮框架省了什么活。这也是我为什么一直坚持让新人先从 IDEA 手动创建 Servlet 开始的原因——过程越折腾后面的路越顺。