从 Servlet 生命周期到 Tomcat 执行原理:Web 服务的底层逻辑全解析

一、核心概念:规范(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 类只有一个实例):

  1. 实例化:Tomcat 启动时(或首次请求时),通过反射创建 Servlet 实例;
  2. 初始化 :调用 init() 方法(仅执行 1 次),用于加载配置、建立数据库连接等;
  3. 处理请求 :每次请求触发 service() 方法(自动分发到 doGet ()/doPost ()),多线程并发执行;
  4. 销毁 :Tomcat 关闭前调用 destroy() 方法(仅执行 1 次),释放资源(如关闭数据库连接)。

三、Tomcat 处理 HTTP 请求的全流程

Tomcat 核心架构是「连接器(处理网络通信)+ 容器(管理 Servlet)」,处理请求分为 5 个核心阶段:

阶段 1:Tomcat 启动 - Servlet 预加载

Tomcat 启动时完成 Servlet 管理准备,避免请求时的性能开销:

  1. 扫描:遍历项目目录,识别带 @WebServlet 注解的类;
  2. 解析:读取注解中的 urlPatterns(访问路径)等配置;
  3. 注册:用 HashMap 存储 Servlet(Key = 访问路径,Value=Servlet 实例);
  4. 初始化:调用每个 Servlet 的 init() 方法。

阶段 2:网络通信 - Socket 监听与请求解析

  1. 端口监听:Tomcat 通过 ServerSocket 监听 8080 端口,等待客户端连接;
  2. 建立连接:客户端请求到来时,创建 Socket 建立 TCP 连接;
  3. 读取报文:通过 InputStream 读取 HTTP 请求报文;
  4. 封装对象:将报文解析为 HttpServletRequest(包含请求行、请求头、请求体、客户端信息)。

阶段 3:资源匹配 - 路由判断

Tomcat 根据请求路径匹配资源,优先级:Servlet > 静态资源 > 404:

  1. 匹配 Servlet:以请求路径为 Key 查询 HashMap,匹配到则执行 Servlet 逻辑;
  2. 匹配静态资源:未匹配到 Servlet 时,检查 webapp 目录下的静态文件(.html/.css/.js);
  3. 404 处理:均未匹配时,返回 404 错误。

阶段 4:业务处理 - Servlet 方法调用

  1. 请求分发:根据 HttpServletRequest 中的请求方法,调用 Servlet 的 doGet ()/doPost ();
  2. 业务执行:Servlet 通过 HttpServletRequest 获取请求参数,通过 HttpServletResponse 设置响应数据。

阶段 5:响应返回 - 封装并发送报文

  1. 封装响应:将 HttpServletResponse 中的数据(状态码、响应头、响应体)封装为 HTTP 响应报文;
  2. 写入流:通过 OutputStream 将报文写入 Socket,返回给客户端;
  3. 连接管理: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();
    }
}

运行测试

  1. 启动 MiniTomcat 主类;
  2. 浏览器访问 http://localhost:8080/hello,可看到 Hello MiniTomcat!
  3. 访问 http://localhost:8080/xxx,返回 404 错误。

六、总结

核心要点

  1. Servlet 是规范:定义了 Web 组件的生命周期(init-service-destroy)和 HTTP 请求处理标准,分层设计(接口→抽象类→HTTP 适配→业务实现)降低开发成本;
  2. Tomcat 是容器:实现 Servlet 规范,负责网络通信(Socket)、Servlet 管理(扫描 / 注册 / 初始化)、请求分发(匹配 Servlet / 静态资源);
  3. 核心协作逻辑:Tomcat 启动时注册 Servlet → 监听端口接收请求 → 解析请求并匹配 Servlet → 调用 Servlet 处理业务 → 封装响应返回客户端 → 关闭时销毁 Servlet。
相关推荐
wxjlkh13 小时前
5分钟部署Docker!Rocky Linux极速安装+一键加速配置脚本
云原生·eureka
liliangcsdn2 天前
centos7 docker镜像库国内加速
云原生·eureka
西柚小萌新2 天前
【docker】--4.Docker Compose
docker·容器·eureka
隔壁小邓2 天前
docker从入门到实践的全面教程
docker·容器·eureka
kaixin_啊啊2 天前
(1)如何使用Docker部署IT-Tools并结合内网穿透实现公网访问本地工具箱服务
docker·容器·eureka
灰阳阳2 天前
Docker-镜像-命令清单
java·docker·eureka
尘世壹俗人2 天前
知识点4---Docker命令使用
docker·容器·eureka
灰阳阳2 天前
docker基础命令讲解
运维·docker·容器·eureka
⑩-3 天前
服务注册与发现的原理?Nacos vs Eureka?
spring cloud·云原生·eureka