一、核心概念:规范(Servlet)与容器(Tomcat)
- Servlet :Java 定义的处理 HTTP 请求的标准接口 / 规范,规定了 Web 组件的生命周期、请求处理方式,是开发者与容器之间的 "契约"。
- Tomcat :Servlet 规范的工业级实现(容器),负责网络通信、Servlet 实例管理、请求分发等底层工作,让开发者无需关注网络和容器细节,只需专注业务逻辑。
二、Servlet 生命周期与分层设计
2.1 Servlet 体系结构(分层设计)
遵循「规范定义 → 通用实现 → 协议适配 → 业务定制」的分层逻辑,降低开发复杂度:
| 层级 | 作用 | 核心方法 / 特性 |
|---|---|---|
| Servlet 接口 | 定义生命周期核心契约(规范层) | init()、service()、destroy()、getServletConfig()、getServletInfo() |
| GenericServlet | 抽象实现类(通用层),简化开发 | 实现除 service () 外的所有接口方法,隐藏容器交互细节 |
| HttpServlet | HTTP 协议适配层(继承 GenericServlet) | 重写 service (),按请求方法(GET/POST)分发到 doGet ()/doPost () |
| 自定义 Servlet | 业务实现层 | 继承 HttpServlet,重写 doXxx () 方法,通过 @WebServlet 配置访问路径 |
自定义 Servlet 示例:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 配置访问路径:http://localhost:8080/hello
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
// 重写GET请求处理方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 设置响应编码,避免中文乱码
resp.setContentType("text/html;charset=UTF-8");
// 向客户端输出响应内容
resp.getWriter().write("Hello Servlet! 你好,Servlet!");
}
}
2.2 Servlet 生命周期(容器全管控)
Servlet 实例的 "一生" 完全由 Tomcat 管理,默认单例(一个 Servlet 类只有一个实例):
- 实例化:Tomcat 启动时(或首次请求时),通过反射创建 Servlet 实例;
- 初始化 :调用
init()方法(仅执行 1 次),用于加载配置、建立数据库连接等; - 处理请求 :每次请求触发
service()方法(自动分发到 doGet ()/doPost ()),多线程并发执行; - 销毁 :Tomcat 关闭前调用
destroy()方法(仅执行 1 次),释放资源(如关闭数据库连接)。
三、Tomcat 处理 HTTP 请求的全流程
Tomcat 核心架构是「连接器(处理网络通信)+ 容器(管理 Servlet)」,处理请求分为 5 个核心阶段:
阶段 1:Tomcat 启动 - Servlet 预加载
Tomcat 启动时完成 Servlet 管理准备,避免请求时的性能开销:
- 扫描:遍历项目目录,识别带
@WebServlet注解的类; - 解析:读取注解中的
urlPatterns(访问路径)等配置; - 注册:用 HashMap 存储 Servlet(Key = 访问路径,Value=Servlet 实例);
- 初始化:调用每个 Servlet 的
init()方法。
阶段 2:网络通信 - Socket 监听与请求解析
- 端口监听:Tomcat 通过
ServerSocket监听 8080 端口,等待客户端连接; - 建立连接:客户端请求到来时,创建
Socket建立 TCP 连接; - 读取报文:通过
InputStream读取 HTTP 请求报文; - 封装对象:将报文解析为
HttpServletRequest(包含请求行、请求头、请求体、客户端信息)。
阶段 3:资源匹配 - 路由判断
Tomcat 根据请求路径匹配资源,优先级:Servlet > 静态资源 > 404:
- 匹配 Servlet:以请求路径为 Key 查询 HashMap,匹配到则执行 Servlet 逻辑;
- 匹配静态资源:未匹配到 Servlet 时,检查 webapp 目录下的静态文件(.html/.css/.js);
- 404 处理:均未匹配时,返回 404 错误。
阶段 4:业务处理 - Servlet 方法调用
- 请求分发:根据
HttpServletRequest中的请求方法,调用 Servlet 的 doGet ()/doPost (); - 业务执行:Servlet 通过
HttpServletRequest获取请求参数,通过HttpServletResponse设置响应数据。
阶段 5:响应返回 - 封装并发送报文
- 封装响应:将
HttpServletResponse中的数据(状态码、响应头、响应体)封装为 HTTP 响应报文; - 写入流:通过
OutputStream将报文写入 Socket,返回给客户端; - 连接管理:HTTP/1.1 默认长连接,按需保持 / 关闭 Socket 连接。
四、Tomcat 的 I/O 模型
Tomcat 支持多种 I/O 模型,适配不同并发场景:
| I/O 模型 | 特点 | 适用场景 |
|---|---|---|
| BIO | 阻塞 I/O,一个请求一个线程,线程阻塞等待 I/O 完成 | 低并发、简单场景 |
| NIO | 非阻塞 I/O,基于 Reactor 模式,少量线程处理大量请求 | 中高并发(Tomcat 默认) |
| NIO2 | 异步非阻塞 I/O,基于 Proactor 模式,I/O 操作完成后回调 | 高并发、高性能场景 |
五、自定义 MiniTomcat(从 0 实现核心逻辑)
通过简化版 Tomcat 实现,理解底层核心原理,分为 3 个核心步骤:
步骤 1:定义 Servlet 核心接口与适配类
// 1. 模拟 Servlet 核心接口(定义生命周期)
public interface Servlet {
void init(); // 初始化
void service(Request req, Response resp); // 处理请求
void destroy(); // 销毁
}
// 2. 模拟 GenericServlet(通用实现)
public abstract class GenericServlet implements Servlet {
@Override
public void init() {
// 默认初始化逻辑,子类可重写
System.out.println("GenericServlet 初始化");
}
@Override
public void destroy() {
// 默认销毁逻辑,子类可重写
System.out.println("GenericServlet 销毁");
}
}
// 3. 模拟 HttpServlet(HTTP 协议适配)
public abstract class HttpServlet extends GenericServlet {
@Override
public void service(Request req, Response resp) {
// 根据请求方法分发到对应 doXxx 方法
if ("GET".equals(req.getMethod())) {
doGet(req, resp);
} else if ("POST".equals(req.getMethod())) {
doPost(req, resp);
}
}
// 抽象方法,由子类实现具体业务
protected abstract void doGet(Request req, Response resp);
protected abstract void doPost(Request req, Response resp);
}
步骤 2:实现 Request/Response 封装类
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
// 模拟 Request(封装 HTTP 请求)
public class Request {
private String method; // 请求方法:GET/POST
private String path; // 请求路径:如 /hello
// 构造方法:解析 Socket 输入流中的 HTTP 请求报文
public Request(InputStream in) {
try {
// 读取请求报文(简化版,仅解析首行)
byte[] buffer = new byte[1024];
int len = in.read(buffer);
String request = new String(buffer, 0, len);
// 解析请求首行:如 "GET /hello HTTP/1.1"
String firstLine = request.split("\\n")[0];
String[] parts = firstLine.split(" ");
this.method = parts[0]; // GET
this.path = parts[1]; // /hello
} catch (IOException e) {
e.printStackTrace();
}
}
// getter 方法
public String getMethod() { return method; }
public String getPath() { return path; }
}
// 模拟 Response(封装 HTTP 响应)
public class Response {
private OutputStream out; // 关联 Socket 输出流
public Response(OutputStream out) {
this.out = out;
}
// 写入响应内容(封装为 HTTP 响应报文)
public void write(String content) {
try {
// 构建 HTTP 响应报文
String response = "HTTP/1.1 200 OK\n" +
"Content-Type: text/html;charset=UTF-8\n" +
"\n" + // 空行分隔响应头和响应体
content;
// 写入输出流
out.write(response.getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
步骤 3:实现 MiniTomcat 核心类
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
// 自定义业务 Servlet(示例)
class HelloServlet extends HttpServlet {
@Override
protected void doGet(Request req, Response resp) {
resp.write("<h1>Hello MiniTomcat!</h1>");
}
@Override
protected void doPost(Request req, Response resp) {
resp.write("<h1>POST 请求已处理</h1>");
}
}
// MiniTomcat 核心类
public class MiniTomcat {
// Servlet 容器:Key=访问路径,Value=Servlet 实例
private Map<String, Servlet> servletMap = new HashMap<>();
private int port = 8080; // 监听端口
// 初始化 Servlet 容器(模拟 Tomcat 扫描注册)
public void initServletMap() {
// 注册 HelloServlet,访问路径 /hello
servletMap.put("/hello", new HelloServlet());
}
// 启动 MiniTomcat
public void start() {
// 1. 初始化 Servlet 容器
initServletMap();
System.out.println("MiniTomcat 启动,监听端口:" + port);
try (ServerSocket serverSocket = new ServerSocket(port)) {
// 2. 循环监听客户端连接(BIO 模型)
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待连接
// 3. 每个请求分配一个线程处理
new Thread(() -> handleRequest(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 处理单个请求
private void handleRequest(Socket socket) {
try (InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()) {
// 4. 封装 Request 和 Response
Request req = new Request(in);
Response resp = new Response(out);
// 5. 匹配 Servlet
Servlet servlet = servletMap.get(req.getPath());
if (servlet != null) {
servlet.init(); // 初始化
servlet.service(req, resp); // 处理请求
servlet.destroy(); // 销毁(简化版,实际仅关闭时执行)
} else {
// 未匹配到,返回 404
resp.write("<h1>404 Not Found</h1>");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 主方法启动
public static void main(String[] args) {
new MiniTomcat().start();
}
}
运行测试:
- 启动 MiniTomcat 主类;
- 浏览器访问
http://localhost:8080/hello,可看到Hello MiniTomcat!; - 访问
http://localhost:8080/xxx,返回 404 错误。
六、总结
核心要点
- Servlet 是规范:定义了 Web 组件的生命周期(init-service-destroy)和 HTTP 请求处理标准,分层设计(接口→抽象类→HTTP 适配→业务实现)降低开发成本;
- Tomcat 是容器:实现 Servlet 规范,负责网络通信(Socket)、Servlet 管理(扫描 / 注册 / 初始化)、请求分发(匹配 Servlet / 静态资源);
- 核心协作逻辑:Tomcat 启动时注册 Servlet → 监听端口接收请求 → 解析请求并匹配 Servlet → 调用 Servlet 处理业务 → 封装响应返回客户端 → 关闭时销毁 Servlet。