戴老师的博客 - Tomcat 源码
type: channel
link: https://www.tushu.info/category/Tomcat-%E6%BA%90%E7%A0%81/
description: Tomcat 源码
items: 第六章：支持 Session 和 Cookie-Minitomcat
items: 第八章：实现异步请求支持（基于 Servlet 协议）-Minitomcat
items: 第七章：实现多线程支持-Minitomcat
items: 第十五章：支持配置热加载和自动部署-MiniTomcat
items: Java 网络I/O 流基础
items: MiniTomcat 项目大纲
items: Java 文件I/O 基础-MiniTomcat系列课程准备
items: 深入理解Servlet：从基础概念到高级特性与实战应用
items: 第九章：实现过滤器（Filter）和监听器（Listener）-Minitomcat
items: 第十四章：实现 Log 模块-MiniTomcat

第六章：支持 Session 和 Cookie-Minitomcat
type: item
date: 2026-07-07T08:37:27+00:00
description: 本章将实现对 HTTP Session 和 Cookie 的支持，以便维护客户端的会话状态，使服务器能够识别每次请求是否来自同一客户端并跟踪其状态。我们将通过实现一个计数器 Servlet，来记录每个客户端的访问次数，从而验证会话管理功能的有效性。6.1 功能目标实现会话管理：通过 HttpSession 接口支持，为每个客户端分配唯一的 Session ID，并在服务器端保持会话状态。支持 Cookie：实现对请求中 Cookie 的解析，提取相关信息，并能够在响应中设置新的 Cookie 信息（如 JSESSIONID）。6.2 代码结构更新后的 MiniTomcat 项目结构如下。新增了 CustomHttpSession、SessionManager、HttpRequestParser 等相关类，以及用于测试的 CounterServlet 示例。MiniTomcat ├─ src │ ├─ main │ │ ├─ java │ │ │ ├─ com.daicy.minitomcat │ │ │ │ ├─ servlet │ │ │ │ │ ├─ CustomServletOutputStream.java // 自定义的 Servlet 输出流类 │ │ │ │ │ ├─ CustomHttpSession.java // 自定义的 HttpSession 实现 │ │ │ │ │ ├─ HttpServletRequestImpl.java // HTTP 请求的实现类 │ │ │ │ │ ├─ HttpServletResponseImpl.java // HTTP 响应的实现类 │ │ │ │ │ ├─ ServletConfigImpl.java // Servlet 配置的实现类 │ │ │ │ │ ├─ ServletContextImpl.java // Servlet 上下文的实现类 │ │ │ │ ├─ CounterServlet.java // Session 功能 Servlet 示例类 │ │ │ │ ├─ HelloServlet.java // Servlet 示例类 │ │ │ │ ├─ HttpConnector.java // 连接器类 │ │ │ │ ├─ HttpProcessor.java // 请求处理器 │ │ │ │ ├─ HttpServer.java // 主服务器类 │ │ │ │ ├─ HttpRequestParser.java // HttpRequest 信息解析类 │ │ │ │ ├─ ServletLoader.java // Servlet 加载器 │ │ │ │ ├─ ServletProcessor.java // Servlet 处理器 │ │ │ │ ├─ StaticResourceProcessor.java // 静态资源处理器 │ │ │ │ ├─ SessionManager.java // Session 管理器 │ │ │ │ ├─ WebXmlServletContainer.java // Servlet 容器相关类 │ │ ├─ resources │ │ │ ├─ webroot │ │ │ │ ├─ index.html │ │ │ ├─ web.xml │ ├─ test ├─ pom.xml6.3 代码实现6.3.1 创建 CustomHttpSession 类CustomHttpSession 类负责管理每个客户端的会话数据，并为每个会话分配唯一的 Session ID。该类实现了 javax.servlet.http.HttpSession 接口的核心功能。package com.daicy.minitomcat.servlet; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import java.util.*; // 自定义类模拟实现 HttpSession 接口的部分功能 public class CustomHttpSession implements HttpSession { private String id; private Date creationTime; private Date lastAccessedTime; private int maxInactiveInterval; private Map<String, Object> attributes = new HashMap<>(); public CustomHttpSession(String sessionId.htm…

第八章：实现异步请求支持（基于 Servlet 协议）-Minitomcat
type: item
date: 2026-07-07T08:37:20+00:00
description: 第八章：实现异步请求支持（基于 Servlet 协议）在本章中，我们将进一步扩展 MiniTomcat，加入对 Servlet 规范 的支持，特别是 异步请求处理 功能。Servlet 3.0 引入了异步处理机制，允许请求在后台线程中异步执行，从而避免阻塞主线程。这一机制能显著提高服务器处理效率，尤其适用于处理耗时较长的任务（如文件上传、大数据处理或外部 API 调用等）。8.1 功能目标本章主要实现以下目标：实现 Servlet 3.0 异步请求处理：基于 Servlet 3.0 标准构建异步支持能力。提供 AsyncContext 机制：通过 AsyncContext 管理异步请求和响应的生命周期，允许后台线程处理长时间任务而不阻塞主线程。支持异步响应：确保异步任务完成后，能够正确地向客户端发送响应数据。8.2 异步处理流程异步请求的处理流程主要包含以下三个阶段：启动异步请求：客户端发起请求后，Servlet 容器检查请求是否需要异步处理。若需要，调用 request.startAsync() 启动异步上下文。异步任务执行：Servlet 容器将请求处理逻辑分配到后台线程执行，主线程立即返回并释放资源，继续处理其他请求。完成异步处理：后台线程执行完任务后，调用 AsyncContext.complete() 结束异步处理，并向客户端发送响应。8.3 代码实现8.3.1 修改 HttpServletRequestImpl 类，支持异步请求为了支持异步请求，我们需要在 HttpServletRequestImpl 中引入 AsyncContext 相关状态，并实现启动异步任务的方法。public class HttpServletRequestImpl implements HttpServletRequest { // ... 其他成员变量 private boolean asyncStarted = false; private AsyncContext asyncContext; public HttpServletRequestImpl(String method, String requestURI, String queryString, Map<String, String> headers.html) { // ... 初始化逻辑 } @Override public boolean isAsyncStarted() { return asyncStarted; } @Override public AsyncContext getAsyncContext() { if (!asyncStarted.html) { throw new IllegalStateException("Async not started".html); } return asyncContext; } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse.html) throws IllegalStateException { if (asyncStarted.html) { throw new IllegalStateException("Async already started".html); } asyncStarted = true; asyncContext = new AsyncContextImpl(this, servletResponse.html); return asyncContext; } @Override public boolean isAsyncSupported() { return asyncStarted; } // ... 其他方法 }8.3.2 修改 HttpProcessor 类，支持异步请求在 HttpProcessor 中，我们需要判断当前请求是否进入了异步模式。如果是异步请求，连接关闭的逻辑需要相应调整，以避免在后台任务完成前过早关闭连接。package com.daicy.minitomcat; import com.daicy.minitomcat.servlet.HttpServletRequestImpl; import com.daicy.minitomcat.servlet.HttpServletResponseImpl; import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletRequest; import java.io.*;…

第七章：实现多线程支持-Minitomcat
type: item
date: 2026-07-07T08:36:00+00:00
description: 第七章：实现多线程支持在本章中，我们将为 MiniTomcat 引入多线程支持，旨在显著提升服务器的并发处理能力。通过引入线程池（Thread Pool）机制，服务器能够同时处理多个客户端请求，避免单线程模型下的请求阻塞问题。这将使服务器在面对高并发场景时更加高效，更好地支持多用户同时访问。7.1 功能目标本章的核心改造目标如下：多线程支持：利用线程池管理线程资源，为每个客户端请求分配独立的线程进行处理。线程池机制：避免为每个请求频繁创建和销毁线程，通过复用线程降低系统开销，提高执行效率。并发处理：支持多个客户端同时访问不同的 Servlet，确保请求之间互不干扰，实现真正的并发处理。7.2 代码结构本次修改将引入线程池机制，主要通过 ExecutorService 来管理工作线程。项目代码结构更新如下：MiniTomcat ├─ src │ ├─ main │ │ ├─ java │ │ │ ├─ com.daicy.minitomcat │ │ │ │ ├─ servlet │ │ │ │ │ ├─ CustomServletOutputStream.java // 自定义的 Servlet 输出流类 │ │ │ │ │ ├─ CustomHttpSession.java // 自定义的 HttpSession │ │ │ │ │ ├─ HttpServletRequestImpl.java // HTTP 请求的实现类 │ │ │ │ │ ├─ HttpServletResponseImpl.java // HTTP 响应的实现类 │ │ │ │ │ ├─ ServletConfigImpl.java // Servlet 配置的实现类 │ │ │ │ │ ├─ ServletContextImpl.java // Servlet 上下文的实现类 │ │ │ │ ├─ CounterServlet.java // Session 功能 Servlet 示例类 │ │ │ │ ├─ HelloServlet.java // Servlet 示例类 │ │ │ │ ├─ HttpConnector.java // 连接器类 │ │ │ │ ├─ HttpProcessor.java // 请求处理器 │ │ │ │ ├─ HttpServer.java // 主服务器类 │ │ │ │ ├─ HttpRequestParser.java // HttpRequest 信息解析类 │ │ │ │ ├─ ServletLoader.java // Servlet 加载器 │ │ │ │ ├─ ServletProcessor.java // Servlet 处理器 │ │ │ │ ├─ StaticResourceProcessor.java // 静态资源处理器 │ │ │ │ ├─ SessionManager.java // Session 管理器 │ │ │ │ ├─ ThreadPool.java // 线程池管理 │ │ │ │ ├─ WebXmlServletContainer.java // Servlet 容器相关类 │ │ ├─ resources │ │ │ ├─ webroot │ │ │ │ ├─ index.html │ │ │ ├─ web.xml │ ├─ test ├─ pom.xml7.3 代码实现7.3.1 创建 ThreadPool 类我们将基于 Java 并发包中的 ThreadPoolExecutor 来实现线程池管理。ThreadPool 类负责封装线程池的生命周期，并提供提交任务的接口。package com.daicy.minitomcat; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class ThreadPool { private ExecutorService executor; public ThreadPool(int poolSize.html) { // 创建固定大小的线程池 executor = Executors.newFixedThreadPool(poolSize.html); } public void submitTask(Runnable task.html) { executor.submit(task.html); } public void shutdown() { if (executor != null.ht…

第十五章：支持配置热加载和自动部署-MiniTomcat
type: item
date: 2026-07-07T08:35:57+00:00
description: 第十五章：支持配置热加载和自动部署功能目标本章旨在为 MiniTomcat 实现以下核心功能：支持热部署（Hot Deployment）：能够在不重启服务器的情况下加载新的 Web 应用。目录监控：监控应用目录的变化，当检测到新的 Web 应用时，自动加载该应用的 Servlet 和资源。配置热加载：支持 web.xml 的重新加载和应用更新，确保配置修改后即时生效。实现内容为达成上述目标，我们将实现以下机制：目录监控机制：当检测到应用目录中的变化（如新增应用、修改或删除文件）时，自动触发加载或卸载操作。配置重载支持：支持 web.xml 文件的重新加载，使得配置更新后能自动生效。文件监控线程：提供一个后台线程，持续检测应用目录中的变化，并在变化发生时触发相关的业务逻辑。15.1 热部署的设计热部署的关键在于动态地检测文件系统的变化，并自动加载或卸载 Web 应用。为此，我们可以借助 Java NIO 提供的 WatchService 接口，实现对应用目录中文件的监控。15.1.1 设计思路文件监控：使用 WatchService 监控应用目录中的文件变化，包括新增、修改或删除操作。自动加载应用：当监控到新的应用目录或文件发生变化时，自动加载或更新应用的 Servlet 和配置。重新加载配置：当 web.xml 文件发生变化时，重新加载该配置文件，并应用新的路由和初始化参数。15.1.2 使用 WatchService 实现文件监控以下是 HotDeployment 类的实现代码，展示了如何利用 WatchService 监听目录事件：package com.daicy.minitomcat; import com.daicy.minitomcat.core.StandardContext; import java.net.URL; import java.nio.file.*; import static com.daicy.minitomcat.HttpServer.CONF_PATH; public class HotDeployment { // private static final String APP_BASE_PATH = "/path/to/apps"; // Web 应用目录 public void startDeploymentMonitor() { try { URL url = getClass().getResource(CONF_PATH.html); Path path = Paths.get(url.getPath(.html)); WatchService watchService = FileSystems.getDefault().newWatchService(); // 注册监控事件：创建、修改和删除 path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); System.out.println("Monitoring directory for changes: " + path.toString(.html)); // 监控目录变化 while (true.html) { WatchKey key; try { key = watchService.take(); // 等待文件变化事件 } catch (InterruptedException e.html) { System.out.println("Monitoring interrupted".html); return; } // 处理监控到的事件 for (WatchEvent<?> event : key.pollEvents(.html)) { WatchEvent.Kind<?> kind = event.kind(); Path filePath = (Path.html) event.context(); System.out.println("Event detected: " + kind + " on file: " + filePath.html); // 根据事件类型进行相应处理 if (kind == StandardWatchEventKinds.ENTRY_CREATE.html) { // 新应用被添加，加载该应用 deployApplication(filePath.html); } else if (kind == StandardWatchEventKind…

Java 网络I/O 流基础
type: item
date: 2026-07-07T08:35:48+00:00
description: 引言在构建 MiniTomcat 的过程中，处理网络请求与数据传输是核心环节，而这正是 Java 的输入/输出（I/O）流所能胜任的关键任务。I/O 流负责数据的读取与写入，是实现客户端与服务器之间数据交互的基础。因此，深入理解 Java I/O 流机制，将有助于我们在 MiniTomcat 中高效地实现 HTTP 请求解析与响应构建功能。1. Java I/O 流概述Java I/O 流体系主要可以分为以下两大类：字节流（Byte Stream）：主要用于处理二进制数据（如图片、音频、网络数据包），核心基类为 InputStream 和 OutputStream。字符流（Character Stream）：主要用于处理字符数据（文本），核心基类为 Reader 和 Writer。在 MiniTomcat 的开发场景中，字节流通常用于底层的网络数据传输，而字符流则更适合处理文本数据，例如读取 HTTP 请求头或写入 HTML 响应内容。2. 字节流（Byte Stream）字节流是 I/O 操作的基础，适用于所有类型的数据。InputStream：用于读取字节数据，常见实现类包括 FileInputStream 和 BufferedInputStream。OutputStream：用于写入字节数据，常见实现类包括 FileOutputStream 和 BufferedOutputStream。示例：从文件中读取字节数据FileInputStream fis = new FileInputStream("example.txt".html); int data; // 逐字节读取，直到文件末尾（返回 -1） while ((data = fis.read(.html)) != -1) { // 注意：直接将 byte 转为 char 仅适用于文本文件演示 System.out.print((char.html) data); } fis.close();3. 字符流（Character Stream）字符流在字节流的基础上增加了编码转换功能，更适合处理文本信息。Reader：用于读取字符数据，常见实现类包括 FileReader 和 BufferedReader。Writer：用于写入字符数据，常见实现类包括 FileWriter 和 BufferedWriter。示例：从文件中读取字符数据FileReader fr = new FileReader("example.txt".html); BufferedReader br = new BufferedReader(fr.html); String line; // 按行读取文本 while ((line = br.readLine(.html)) != null) { System.out.println(line.html); } br.close();4. 缓冲流（Buffered Stream）为了提升 I/O 操作效率，Java 提供了缓冲流包装类。BufferedReader 和 BufferedWriter：用于字符数据的缓冲处理。BufferedInputStream 和 BufferedOutputStream：用于字节数据的缓冲处理。在服务器开发中，直接使用底层流会导致频繁的系统调用。缓冲流通过在内存中建立缓冲区，减少了实际 I/O 操作的次数，能显著提升读取和写入的效率，特别适用于高频的网络 I/O 操作。5. 综合运用：Socket I/O在服务器与客户端之间建立通信时，通常使用 Socket 来传输数据。Socket 的输入/输出流可以通过字节流或字符流来操作，具体取决于数据处理的需求。InputStream 和 OutputStream：可从 Socket 中直接获取，分别用于读取请求二进制数据和发送响应二进制数据。常见的 Socket 操作包括：读取 HTTP 请求头、构建响应内容、关闭连接等。示例：简单的 Socket 读取与响应操作ServerSocket serverSocket = new ServerSocket(8080.html); Socket clientSocket = serverSocket.accept(); // 通过 InputStreamReader 将字节流转换为字符流，便于处理文本协议 BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(.html))); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(cli…

MiniTomcat 项目大纲
type: item
date: 2026-07-07T08:35:34+00:00
description: 上一章内容：打造属于你的 MiniTomcat：深入理解 Web 容器核心架构与实现之路从零开始实现一个类似 Tomcat 的轻量级 Java Web 容器，可以分为多个步骤逐步构建核心功能。以下是一个详细的实现路径大纲，涵盖了每一步的目标功能，旨在帮助你从简单的 HTTP 服务器起步，逐步深入实现基本的 Servlet 容器。第 1 章：实现基础 HTTP 服务器功能目标创建一个简单的 HTTP 服务器，能够监听指定端口、接收 HTTP 请求，并返回 HTTP 响应。实现内容使用 Java 的 ServerSocket 监听指定端口。读取客户端请求，解析 HTTP 请求行和头部信息。返回简单的 HTTP 响应，例如 200 OK 或 404 Not Found。示例功能访问 http://localhost:8080/ 时，返回一段静态文本。学习收获理解 HTTP 协议的基本结构，包括请求和响应的格式。第 2 章：解析 HTTP 请求，支持静态文件功能目标解析 HTTP 请求中的路径、方法（GET、POST 等）和头部信息。基于请求路径提供静态文件支持，类似于静态资源服务器。实现内容根据 URL 路径，查找服务器本地的文件并返回其内容（如 HTML、CSS、JS 文件）。支持 MIME 类型识别，如 text/html、application/javascript，返回正确的 Content-Type。示例功能访问 http://localhost:8080/index.html 时，返回服务器上的 index.html 文件内容。学习收获加深对 HTTP 请求和响应头的理解，熟悉静态文件的处理方式。第 3 章：实现连接器（Connector）组件功能目标实现一个连接器组件，用于处理 HTTP 连接和数据包解析。该组件用于管理网络传输和请求解析，将网络传输与请求处理解耦，提升代码清晰度和容错性。实现内容创建 Connector 组件，用于与客户端建立连接，读取 HTTP 数据包，并交由请求解析器处理。第 4 章：实现 Servlet 容器的基本功能功能目标实现一个简单的 Servlet 容器，支持加载、初始化、调用和销毁 Servlet。实现内容实现 HttpServletRequest 和 HttpServletResponse 类，用于封装请求和响应数据。根据请求路径映射到对应的 Servlet 类，并调用 Servlet 的 service() 方法处理请求。提供 ServletContext 和 ServletConfig 支持，维护 Servlet 的生命周期。示例功能定义一个简单的 Servlet，如 HelloServlet，访问时返回自定义的响应内容。学习收获熟悉 Servlet 的生命周期（init、service、destroy），理解 Servlet 和 Web 容器之间的交互关系。第 5 章：支持 Servlet 配置和 URL 映射功能目标通过配置文件（例如 web.xml）管理 Servlet 路由映射和初始化参数。实现内容解析 web.xml 文件，读取 Servlet 映射信息，将 URL 路径映射到对应的 Servlet 类。支持 Servlet 的初始化参数，并在 ServletConfig 中提供获取方式。示例功能根据 web.xml 的配置，访问 /hello 路径时自动调用 HelloServlet。学习收获学习解析 XML 文件，掌握基于配置的 URL 路由映射。第 6 章：支持 Session 和 Cookie功能目标增加对 Session 和 Cookie 的支持，实现简单的会话管理。实现内容实现 HttpSession，为每个客户端会话分配唯一的 Session ID，并保持会话状态。实现对 Cookie 的支持，解析请求中的 Cookie，并将响应的 Cookie 发送到客户端。示例功能实现一个简单的计数器 Servlet，记录每个客户端的访问次数。学习收获掌握会话管理的基本原理，理解如何使用 Session 和 Cookie 维护客户端状态。第 7 章：实现多线程支持功能目标支持多线程处理，提高服务器并发能力，允许多个请求同时处理。实现内容使用线程池处理每个客户端的连接，避免阻塞其他请求。可以使用 ExecutorService 或 ThreadPoolExecutor 创建线程池。示例功能多个客户端可以同时访问不同的 Servlet，不会互相影响。学习收获了解多线程编程和线程池的概念，掌握如何设计并发安全的 Web 服务器。第 8 章：实现异步请求支持功能目标在多线程支持的基础上，引入异步请求处理。异步请求能够在高并发和长时间请求（如大文件上传、长连接）场景下提升处理效…

Java 文件I/O 基础-MiniTomcat系列课程准备
type: item
date: 2026-07-07T08:35:05+00:00
description: 我们将以 Java I/O 的基础知识为起点，为构建 MiniTomcat 打下坚实的文件处理与网络输入输出基础。在本模块中，你将深入了解 Java 的 I/O 系统体系，学会使用字节流和字符流进行高效的数据传输，并掌握常见的文件管理操作。1. Java I/O 概览Java 中的 I/O（Input/Output，输入/输出）操作允许程序与外部资源（如文件系统、网络套接字等）进行数据交互。Java 的 I/O 系统主要基于流（Stream）的概念，分为两种核心方式：字节流（Byte Stream）：以字节为单位处理数据，适用于传输图像、音频、视频等二进制内容。字符流（Character Stream）：以字符为单位处理数据，内置了字符编码转换机制，更适合处理文本文件。2. 字节流与字符流2.1 字节流 (InputStream 和 OutputStream.html)字节流是 I/O 操作的基础，直接处理原始二进制数据。其主要抽象基类为 InputStream 和 OutputStream。常用的具体实现类包括：FileInputStream：用于从文件中读取字节数据。FileOutputStream：用于向文件中写入字节数据。2.2 字符流 (Reader 和 Writer.html)字符流在字节流的基础上进行了封装，能够自动处理字符编码问题，简化了文本操作。其主要抽象基类为 Reader 和 Writer。常用的具体实现类包括：FileReader：用于从文件中读取字符数据。FileWriter：用于向文件中写入字符数据。3. 文件处理操作在 Java 中，可以通过 java.io.File 类来管理文件和目录的元数据。常见的操作包括：创建文件：file.createNewFile()删除文件：file.delete()判断文件是否存在：file.exists()获取文件路径：file.getPath()4. 代码示例以下代码演示了如何使用字符流进行基本的文件写入与读取操作。示例中使用了 try-with-resources 语法，确保流在使用完毕后自动关闭，避免资源泄露。import java.io.*; public class FileIOExample { public static void main(String[] args.html) { // 写入文件 try (FileWriter writer = new FileWriter("example.txt".html)) { writer.write("Hello, MiniTomcat!".html); } catch (IOException e.html) { e.printStackTrace(); } // 读取文件 try (FileReader reader = new FileReader("example.txt".html)) { int character; // 逐字符读取，直到文件末尾（返回 -1） while ((character = reader.read(.html)) != -1) { System.out.print((char.html) character); } } catch (IOException e.html) { e.printStackTrace(); } } }5. 实践任务 📝为了巩固所学知识，请完成以下练习：字节流练习：创建一个新的 Java 类，尝试使用 FileInputStream 和 FileOutputStream 来复制一个二进制文件（如图片），体会字节流的处理方式。文件管理练习：使用 File 类创建一个指定目录，并在该目录下创建一个文件，将简单的文本内容写入其中。完成这些练习后，你将掌握 Java I/O 的基础核心，并为后续 MiniTomcat 的文件处理与网络通信模块做好准备！如果在实践中遇到问题，欢迎随时提问 🦌说明：本文示例代码使用了 try-with-resources 语法，需确保 Java 版本为 Java 7 或以上。此外，FileWriter 默认使用系统编码，在生产环境中处理文本时，建议显式指定字符编码（如 UTF-8）以避免乱码问题。

深入理解Servlet：从基础概念到高级特性与实战应用
type: item
date: 2026-07-07T08:35:00+00:00
description: 一、Servlet 简介与工作原理Servlet 是 Java Web 开发的核心组件，运行于服务器端，主要负责处理客户端请求并返回响应。其工作原理涉及多个组件的协同运作，从客户端发起请求到服务器端处理并返回响应，整个过程严谨有序。（一）Servlet 容器与 TomcatServlet 容器（Servlet Container）是 Servlet 运行的环境，负责管理 Servlet 的生命周期、资源分配以及请求分发等工作。Apache Tomcat 是最常用的 Servlet 容器之一，具备强大的功能和良好的性能。在 Tomcat 架构中，Context 容器直接管理 Servlet 的包装类 Wrapper，一个 Context 对应一个 Web 应用。例如，在 Tomcat 的配置文件中，可以通过 <Context> 标签配置 Web 应用的相关参数，如上下文路径、文档根目录等。（二）Servlet 的生命周期Servlet 的生命周期由容器管理，主要包含以下四个阶段：加载和实例化Servlet 容器通常在启动时或首次接收到请求时加载 Servlet 类。它通过类加载器从本地文件系统、远程文件系统或网络服务中获取类定义。例如，Web 应用启动时，Tomcat 会根据 web.xml 中的配置找到对应的 Servlet 类并加载。容器使用 Java 反射 API 创建 Servlet 实例，调用默认构造方法（无参构造）。因此，编写 Servlet 类时不应提供带参数的构造方法。初始化实例化后，容器调用 Servlet 的 init() 方法进行初始化。在此方法中，Servlet 可执行准备工作，如建立数据库连接、获取配置信息等。示例如下：public void init(ServletConfig config.html) throws ServletException { super.init(config.html); // 在这里进行初始化操作，如获取初始化参数 String paramValue = config.getInitParameter("paramName".html); // 其他初始化逻辑 }每个 Servlet 实例的 init() 方法仅被调用一次。初始化期间可使用 ServletConfig 对象获取 web.xml 中配置的初始化参数。若发生错误，可抛出 ServletException 或 UnavailableException 异常通知容器。请求处理容器调用 Servlet 的 service() 方法处理请求。在 service() 方法中，Servlet 通过 ServletRequest 对象获取客户端和请求信息，处理后通过 ServletResponse 对象设置响应信息。例如，处理登录请求的 Servlet 如下：protected void service(HttpServletRequest request, HttpServletResponse response.html) throws ServletException, IOException { // 获取用户名和密码 String username = request.getParameter("username".html); String password = request.getParameter("password".html); // 进行登录验证等业务逻辑处理 if (isValidUser(username, password.html)) { response.getWriter().println("登录成功".html); } else { response.getWriter().println("登录失败".html); } }若 service() 方法执行期间发生错误，可抛出 ServletException 或 UnavailableException 异常。若 UnavailableException 指示实例永久不可用，容器将调用 destroy() 方法释放实例。服务终止当容器检测到 Servlet 实例应被移除时（如应用停止或重新加载），调用 destroy() 方法释放资源，如关闭数据库连接、保存数据等。示例如下：public void destroy() { // 释放资源的逻辑，如关闭数据库连接 if (connection != null.html) { try { connection.close(); } catch (SQLException e.html) { e.printStackTrace(); } } super.destroy(); }dest…

第九章：实现过滤器（Filter）和监听器（Listener）-Minitomcat
type: item
date: 2026-07-07T08:33:37+00:00
description: 第九章：实现过滤器（Filter）和监听器（Listener）本章目标本章旨在为 Minitomcat 容器增加 Filter（过滤器） 和 Listener（监听器） 的支持，以便在请求处理过程中插入额外的操作或监听特定事件。具体实现目标如下：实现 Filter 接口：支持在请求到达 Servlet 之前和响应返回客户端之前插入自定义逻辑，例如日志记录、认证拦截等。实现 Listener 接口：支持监听 Servlet 上下文（Context）、会话（Session）或请求的生命周期事件。示例功能：实现一个日志过滤器，记录每个请求的访问时间和路径。在 Servlet 上下文初始化时创建监听器，监听上下文和会话的创建与销毁事件。9.1 过滤器（Filter）Filter 是 Servlet 容器提供的一个接口，它允许开发者在请求到达 Servlet 之前和响应返回客户端之前插入自定义的处理逻辑。过滤器常用于执行日志记录、权限验证、请求重定向、输入输出数据处理等通用任务。1. Filter 接口的实现标准的 Filter 接口主要包含三个方法：init(FilterConfig config.html)：初始化过滤器。doFilter(ServletRequest request, ServletResponse response, FilterChain chain.html)：执行过滤器的核心逻辑，并将请求传递给下一个过滤器或目标 Servlet。destroy()：销毁过滤器，释放资源。2. 日志过滤器的实现下面实现一个简单的日志过滤器，用于记录每个请求的访问时间和请求路径。package com.daicy.minitomcat; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; public class LoggingFilter implements Filter { @Override public void init(FilterConfig filterConfig.html) throws ServletException { // 初始化操作，如果有需要可以在这里读取配置参数等 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain.html) throws IOException, ServletException { long startTime = System.currentTimeMillis(); HttpServletRequest request = (HttpServletRequest.html) servletRequest; System.out.println("Request started at: " + startTime + " for path: " + request.getRequestURI(.html)); // 将请求传递给下一个过滤器或目标 Servlet filterChain.doFilter(servletRequest, servletResponse.html); long endTime = System.currentTimeMillis(); System.out.println("Request completed at: " + endTime + " for path: " + request.getRequestURI(.html) + " Took: " + (endTime - startTime.html) + "ms"); } @Override public void destroy() { // 清理资源操作 } }在 LoggingFilter 中，doFilter() 方法记录了请求的开始时间、路径，并通过 chain.doFilter() 将请求传递给链中的下一个节点。过滤器执行完毕后，记录结束时间及总耗时。3. FilterChain 接口的实现为了实现过滤器的链式调用，我们需要设计一个 FilterChain 结构。它能够依次调用过滤器列表中的每个 Filter，直到最后一个过滤器或者目标 Servlet 被调用。FilterChain 设计概念FilterChain 是责任链模式的实现，每个过滤器执行完后，调用链中的下一个过滤器。doFilt…

第十四章：实现 Log 模块-MiniTomcat
type: item
date: 2026-07-07T08:33:15+00:00
description: 第十四章：实现 Log 模块功能目标与实现内容本章旨在为 MiniTomcat 实现一个基础的 Log 模块，主要目标包括：日志记录：支持记录请求日志、错误日志和系统日志。级别管理：支持不同的日志级别（INFO、DEBUG、ERROR 等），以便控制日志的详细程度。实现方式：设计一个简单的 Logger 接口及实现类，提供不同级别的日志输出，并支持配置输出格式和文件路径。14.1 日志记录的重要性在 Web 应用开发中，日志记录是至关重要的环节。日志帮助我们跟踪系统的运行状态、诊断问题，并提供可用的监控信息。常见的日志类型包括：请求日志：记录每个 HTTP 请求的相关信息。错误日志：记录程序异常和错误信息。系统日志：记录系统级别的信息，如服务启动、停止等事件。为了更高效地记录日志，我们需要将日志分为不同的级别，并根据日志级别来控制日志内容的输出。14.2 日志级别的设计常见的日志级别如下，我们可以通过控制日志级别来决定输出多少日志信息，避免在生产环境中输出过多的调试信息：DEBUG：最详细的日志，用于调试阶段，记录系统的详细信息。INFO：常规信息，记录系统的正常操作，如请求处理过程等。WARN：警告信息，用于记录可能出现问题的地方，但不一定会导致错误。ERROR：错误信息，用于记录异常或错误，系统无法继续运行的情况。FATAL：严重错误，通常导致系统终止。14.3 实现 Logger 类实现步骤1. 定义日志级别枚举首先定义日志级别的枚举类：public enum LogLevel { DEBUG, INFO, WARN, ERROR, FATAL }2. 创建日志记录器接口定义 Logger 接口，规范日志记录的行为：package com.daicy.minitomcat.log; public interface Logger { void log(LogLevel level, String message.html); void log(LogLevel level, String message, Throwable throwable.html); // debug 级别日志 void debug(String message.html); // info 级别日志 void info(String message.html); // warn 级别日志 void warn(String message.html); // error 级别日志 void error(String message.html); }3. 创建抽象日志类创建 AbstractLogger 抽象类，实现公共逻辑（如级别判断、时间格式化）：package com.daicy.minitomcat.log; import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public abstract class AbstractLogger implements Logger { final LogLevel minLogLevel; protected AbstractLogger(LogLevel minLogLevel.html) { this.minLogLevel = minLogLevel; } private String throwableToString(Throwable throwable.html) { StringBuilder sb = new StringBuilder(); for (StackTraceElement element : throwable.getStackTrace(.html)) { sb.append("\tat ".html).append(element.html).append("\n".html); } return sb.toString(); } String getCurrentTime() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss".html); return LocalDateTime.now().format(formatter.html); } @Override public void log(LogLevel level, String message, Throwable throwable.html) { if (level.ordinal(.html) >= minLogLevel.ordin…