学习目标与前置
建议用时:60-90 分钟准备:已安装 JDK、Maven,Tomcat(外置)或 Maven Tomcat 插件
- 理解 Servlet 容器职责,内嵌 vs 外置差异。
- 编写 TodoServlet 处理 GET/POST。
- 编写 Filter 记录请求日志;JSP 输出页面并对比 JSON。
Step 1:创建 Maven Web 项目
mvn archetype:generate ^
-DgroupId=demo ^
-DartifactId=servlet-demo ^
-DarchetypeArtifactId=maven-archetype-webapp ^
-DinteractiveMode=false
cd servlet-demo
目录要点:src/main/java 写 Servlet/Filter,src/main/webapp 放 JSP 和静态资源,pom.xml 定义依赖与插件。
Step 2:添加 Servlet API 依赖与 Tomcat 插件
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/servlet-demo</path>
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
说明:scope=provided 表示运行时由容器提供;Tomcat 插件方便本地跑。
Tomcat 介绍与工作原理
Tomcat 是 Apache 软件基金会维护的开源 Servlet/JSP 容器,负责解析 HTTP/HTTPS 请求、分发线程、执行 Servlet 代码并返回响应。它提供了 Web Server(连接器)与 Servlet Engine(容器)两部分:前者监听端口接收请求,后者根据 URL 映射把请求交给具体的 Servlet、Filter、JSP。
- 启动与目录结构:bin 目录存放启动脚本,conf 内包括 server.xml(端口、虚拟主机)、web.xml(全局 Servlet 映射)、context.xml(数据源等);webapps 下的每个子目录或 war 文件对应一个 Web 应用。
- 请求处理流程:Connector 读取请求 → 创建 HttpServletRequest/Response 对象 → 依次经过 Filter 链 → 调用 Servlet service/doGet/doPost → 可转发到 JSP → 生成响应并写回客户端。
- 线程与会话:每个请求默认由线程池处理,可通过 server.xml 调整最大线程数;容器负责创建 HttpSession,提供黏性会话、持久化等策略。
- 部署方式:支持解压 war、保留 war 直接热部署、或者通过 Maven/Gradle 插件远程发布;日志位于 logs 目录,可通过 catalina.out、localhost_access_log 查询运行状态。
- 常见配置:server.xml 中的 <Connector> 可切换 NIO/NIO2/Apr 协议,context.xml 能声明 JNDI 数据源、环境变量,web.xml 可统一配置编码过滤器、安全约束。
理解这些概念后,在本地只需把 war 放入 webapps 并启动 Tomcat,即可用浏览器验证 Servlet/JSP 行为,调试和线上部署流程也都会保持一致。
在 IntelliJ IDEA 中使用 Tomcat
- 准备工作:确认使用 IntelliJ IDEA Ultimate(社区版没有应用服务器集成功能),并提前下载好独立的 Tomcat。导入或创建 Maven Web 项目后检查
pom.xml中存在 war 打包配置。 - 创建运行配置:依次点击 Run > Edit Configurations > “+” > Tomcat Server > Local,首次创建会提示选择 Tomcat 安装目录(即解压目录)。IDEA 会自动识别 bin、conf 并创建默认配置。
- 部署应用:切换到 Deployment 标签,点击 “+” 选择 “Artifact” 或 “war exploded”,一般建议选择
project-name:war exploded方便热部署;Context path 建议与项目名一致(如/servlet-demo)。 - 调优与调试:在 Server 标签可修改端口、JRE、VM options;在 “Before launch” 中添加 “Build” 或 “Build Artifacts” 确保每次运行前自动编译。点击 Debug 运行即可在 Servlet 的
doGet/doPost设置断点单步排查。 - 验证与热部署:启动后 IDEA 控制台会输出 Tomcat 日志,浏览器访问
http://localhost:8080/servlet-demo进行验证。修改 JSP 或类后点击 “Update classes and resources” 可热更新,避免频繁重启。
IDEA 对 Tomcat 的一体化支持覆盖了启动、调试、日志查看、热部署和远程部署(可在 Deployment 中配置 SFTP/FTP)。熟练这些操作可以显著缩短“改代码 → 构建 → 启动 → 验证”的迭代周期。
Step 3:编写 Servlet 与 Filter
flowchart LR A[浏览器请求] --> B[Filter 日志/鉴权] B --> C[Servlet 处理 GET/POST] C --> D1[JSON 响应] C --> D2[JSP 渲染转发]
// src/main/java/demo/web/TodoServlet.java
@WebServlet("/todos")
public class TodoServlet extends HttpServlet {
private final List<String> todos = new ArrayList<>();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
String title = req.getParameter("title");
todos.add(title);
resp.setContentType("application/json;charset=UTF-8");
resp.getWriter().write("{\"size\":" + todos.size() + "}");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("application/json;charset=UTF-8");
resp.getWriter().write(new ObjectMapper().writeValueAsString(todos));
}
}
// src/main/java/demo/web/LogFilter.java
@WebFilter("/*")
public class LogFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest r = (HttpServletRequest) req;
long t0 = System.nanoTime();
System.out.println("REQ " + r.getMethod() + " " + r.getRequestURI());
chain.doFilter(req, res);
System.out.println("COST " + (System.nanoTime() - t0)/1_000_000 + "ms");
}
}
Step 4:编写 JSP 对比输出
<%-- src/main/webapp/index.jsp --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<body>
<h3>JSP 渲染 todos</h3>
<ul>
<% java.util.List list = (java.util.List) request.getAttribute("todos"); %>
<% if(list != null) { for(Object t: list){ %>
<li><%= t %></li>
<% }} %>
</ul>
</body>
</html>
对比:JSP 用于服务端渲染;JSON 用于前后端分离,现代项目更常用后者。
Step 5:运行
- 用插件:
mvn tomcat7:run,访问http://localhost:8080/servlet-demo/todos。 - 外置 Tomcat:
mvn package生成 war,放入 Tomcat webapps,启动后访问同路径。
操作步骤(课堂演示)
- POST /todos 新增一条,刷新 GET /todos 查看 JSON 列表。
- 在 Filter 中打印耗时,刷新查看日志。
- 访问 JSP 页面,展示列表,说明 JSON/前后端分离的优势。
常见问题与排查
- 404:检查 @WebServlet 路径或 web.xml 映射。
- 编码问题:POST 表单需
req.setCharacterEncoding("UTF-8"),响应设application/json;charset=UTF-8。 - 端口被占用:修改插件 port,或关闭占用程序。
课堂练习
- 添加删除接口:POST /todos?action=del&index=0 或 DELETE /todos/{id}。
- 在 Filter 中加入简单鉴权:若 header 不含 X-Token,则 401。
课后巩固
- 将 Filter 日志改为“时间 | 方法 | 路径 | 耗时 ms”格式。
- 在 JSP 页面加入表单,提交给 Servlet,再回显结果。