手写简易 Tomcat,理解 Servlet 容器核心原理
·
标题
手写简易 Tomcat,理解 Servlet 容器核心原理
摘要
Tomcat 是 Java Web 开发的核心容器,但其底层原理常让开发者感到抽象。本文通过手写一个简易版 Tomcat,拆解 Servlet 容器的核心逻辑(Socket 通信、Servlet 加载、请求分发),帮助读者从底层理解 Tomcat 的工作机制。
正文
一、Tomcat 核心原理前置知识
Tomcat 本质是一个基于 Socket 的 HTTP 服务器,核心职责:
- 监听指定端口,接收客户端 HTTP 请求;
- 解析 HTTP 请求,匹配对应的 Servlet;
- 调用 Servlet 的 service 方法处理请求;
- 封装 HTTP 响应,返回给客户端。
Servlet 容器的核心:一个 HashMap,key 为 Servlet 访问路径(如 /hello),value 为 Servlet 实例。
二、核心步骤 1:定义 Servlet 接口(模拟标准 Servlet)
标准 Servlet 接口包含 init ()、service ()、destroy () 三个核心方法,我们先定义简化版:
java
运行
/**
* 模拟Servlet接口
*/
public interface MyServlet {
// 初始化
void init();
// 处理请求
void service(MyRequest request, MyResponse response);
// 销毁
void destroy();
}
三、核心步骤 2:封装请求和响应对象
模拟 HTTP 请求和响应,简化版只需解析请求路径、封装响应内容:
java
运行
/**
* 模拟请求对象
*/
public class MyRequest {
private String method; // 请求方法GET/POST
private String path; // 请求路径
public MyRequest(InputStream inputStream) throws IOException {
// 读取请求数据
String content = "";
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if (len > 0) {
content = new String(buffer, 0, len);
}
// 解析请求行(如GET /hello HTTP/1.1)
String line = content.split("\n")[0];
String[] arr = line.split(" ");
this.method = arr[0];
this.path = arr[1];
}
public String getMethod() {
return method;
}
public String getPath() {
return path;
}
}
/**
* 模拟响应对象
*/
public class MyResponse {
private OutputStream outputStream;
public MyResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
// 封装HTTP响应
public void write(String content) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\n")
.append("Content-Type: text/html;charset=UTF-8\n")
.append("\n")
.append(content);
outputStream.write(sb.toString().getBytes());
}
}
四、核心步骤 3:实现自定义 Servlet
编写一个示例 Servlet,处理 /hello 路径的请求:
java
运行
public class HelloServlet implements MyServlet {
@Override
public void init() {
// 初始化逻辑(如加载配置、连接数据库)
System.out.println("HelloServlet初始化");
}
@Override
public void service(MyRequest request, MyResponse response) throws IOException {
// 处理请求并返回响应
response.write("<h1>Hello, 简易Tomcat!</h1>");
}
@Override
public void destroy() {
// 销毁逻辑(如释放资源)
System.out.println("HelloServlet销毁");
}
}
五、核心步骤 4:实现 Servlet 容器(核心)
Servlet 容器的核心是维护 “路径 - Servlet” 映射关系,提供两种加载方式:
方式 1:启动时加载(Tomcat 启动慢,首次请求快)
java
运行
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* 简易Tomcat核心类
*/
public class MyTomcat {
// 端口号
private int port = 8080;
// Servlet容器(路径-Servlet映射)
private Map<String, MyServlet> servletMap = new HashMap<>();
// 静态代码块:Tomcat启动时加载Servlet
static {
// 注册Servlet
servletMap.put("/hello", new HelloServlet());
// 初始化所有Servlet
for (MyServlet servlet : servletMap.values()) {
servlet.init();
}
}
// 启动Tomcat
public void start() throws IOException {
// 监听8080端口
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("简易Tomcat启动,监听端口:" + port);
// 循环接收客户端请求
while (true) {
Socket socket = serverSocket.accept();
// 处理请求(简化版:单线程,实际Tomcat是多线程)
processRequest(socket);
}
}
// 处理请求
private void processRequest(Socket socket) throws IOException {
MyRequest request = new MyRequest(socket.getInputStream());
MyResponse response = new MyResponse(socket.getOutputStream());
// 匹配Servlet
String path = request.getPath();
if (servletMap.containsKey(path)) {
servletMap.get(path).service(request, response);
} else {
response.write("<h1>404 Not Found</h1>");
}
// 关闭资源
socket.close();
}
public static void main(String[] args) throws IOException {
new MyTomcat().start();
}
}
方式 2:首次请求时加载(Tomcat 启动快,首次请求慢)
只需修改 Servlet 加载逻辑,移除静态代码块,在 processRequest 中加载:
java
运行
private void processRequest(Socket socket) throws IOException {
MyRequest request = new MyRequest(socket.getInputStream());
MyResponse response = new MyResponse(socket.getOutputStream());
String path = request.getPath();
// 首次请求时加载Servlet
if (!servletMap.containsKey(path)) {
if ("/hello".equals(path)) {
HelloServlet servlet = new HelloServlet();
servlet.init();
servletMap.put(path, servlet);
} else {
response.write("<h1>404 Not Found</h1>");
socket.close();
return;
}
}
servletMap.get(path).service(request, response);
socket.close();
}
六、运行测试
- 运行 MyTomcat 类,控制台输出 “简易 Tomcat 启动,监听端口:8080”;
- 打开浏览器,访问http://localhost:8080/hello;
- 页面显示 “Hello, 简易 Tomcat!”,控制台输出 “HelloServlet 初始化”。
七、核心原理总结
- Socket 通信:Tomcat 通过 ServerSocket 监听端口,接收客户端 TCP 连接;
- Servlet 容器:本质是 HashMap,维护 “访问路径 - Servlet 实例” 的映射;
- Servlet 生命周期:init ()(初始化)→ service ()(处理请求)→ destroy ()(销毁);
- 加载策略:启动时加载(静态代码块)或首次请求加载,各有优劣。
八、与真实 Tomcat 的差异
本文实现的是极简版 Tomcat,真实 Tomcat 还包含:
- 多线程处理(避免单线程阻塞);
- HTTP 协议完整解析(请求头、请求体、Cookie、Session);
- Servlet 生命周期管理(由容器统一控制 init/destroy);
- 连接器(Connector)、引擎(Engine)、主机(Host)、上下文(Context)等组件;
- 安全校验、异常处理、日志记录等功能。
通过手写简易 Tomcat,我们剥离了复杂的外围组件,聚焦核心逻辑,能更清晰地理解 Tomcat 作为 Servlet 容器的本质 —— 本质是一个处理 HTTP 请求的 Socket 服务器,核心是 Servlet 的加载与请求分发。
更多推荐

所有评论(0)