当前位置: 首页 > news >正文

手写一个Tomcat

Tomcat 是一个广泛使用的开源 Java Servlet 容器,用于运行 Java Web 应用程序。虽然 Tomcat 本身功能强大且复杂,但通过手写一个简易版的 Tomcat,我们可以更好地理解其核心工作原理。本文将带你一步步实现一个简易版的 Tomcat,并深入探讨其核心组件和运行机制。

一、项目概述

Tomcat是一个简易的Java Web服务器,它能够处理HTTP请求并调用相应的Servlet进行处理。项目的核心功能包括:

  • 监听HTTP请求并解析请求内容。

  • 根据请求路径调用相应的Servlet。

  • 支持GET和POST请求方法。

  • 使用注解配置Servlet的URL映射。

  • 通过反射机制动态加载Servlet类。

二、项目结构

首先,我们来看一下项目的整体结构:

项目的主要类及其功能如下:

  • ResponseUtil:用于生成HTTP响应头。

  • SearchClassUtil:扫描指定包下的类文件,获取类的全限定名。

  • WebServlet:自定义注解,用于标记Servlet并指定URL映射。

  • LoginServlet 和 ShowServlet:具体的Servlet实现类,处理不同的HTTP请求。

  • HttpServletRequest 和 HttpServletResponse:模拟HTTP请求和响应对象。

  • GenericServlet 和 HttpServlet:抽象类,提供Servlet的基本实现。

  • Servlet:Servlet接口,定义了Servlet的生命周期方法。

  • MyTomcat:主类,负责启动服务器并处理HTTP请求。

  • ServletConfigMapping:维护URL与Servlet的映射关系。

三、核心组件解析

 1、 ResponseUtil 类

ResponseUtil 类用于生成HTTP响应头。它提供了两个静态方法:

  • getResponseHeader200(String context):生成状态码为200的HTTP响应头,并将响应内容附加到响应头后。

  • getResponseHeader404():生成状态码为404的HTTP响应头。

public class ResponseUtil {public static final String responseHeader200 = "HTTP/1.1 200 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n";public static String getResponseHeader404() {return "HTTP/1.1 404 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n" + "404";}public static String getResponseHeader200(String context) {return "HTTP/1.1 200 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n" + context;}
}

 2、SearchClassUtil 类

 SearchClassUtil 类用于扫描指定包下的类文件,并获取这些类的全限定名。它通过递归遍历目录结构,找到所有以.class结尾的文件,并将其路径转换为类的全限定名。

public class SearchClassUtil {public static List<String> classPaths = new ArrayList<String>();public static List<String> searchClass() {String basePack = "com.qcby.webapps.myweb";String classPath = SearchClassUtil.class.getResource("/").getPath();basePack = basePack.replace(".", File.separator);String searchPath = classPath + basePack;doPath(new File(searchPath), classPath);return classPaths;}private static void doPath(File file, String classpath) {if (file.isDirectory()) {File[] files = file.listFiles();for (File f1 : files) {doPath(f1, classpath);}} else {if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/", "\\").replaceFirst("\\\\", ""), "").replace("\\", ".").replace(".class", "");classPaths.add(path);}}}
}

 3. WebServlet 注解

WebServlet 是一个自定义注解,用于标记Servlet类并指定URL映射。它包含一个urlMapping属性,用于指定Servlet处理的URL路径。

package com.qcby.util;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebServlet {String urlMapping() default "";
}

  4、LoginServlet 和 ShowServlet 类

 LoginServlet 和 ShowServlet 是两个具体的Servlet实现类,分别处理/login/show路径的请求。它们继承自HttpServlet,并重写了doGet方法以处理GET请求。

@WebServlet(urlMapping = "/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是login的doGet方法");response.writeServlet(ResponseUtil.getResponseHeader200("hello"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {}
}@WebServlet(urlMapping = "/show")
public class ShowServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是show");response.writeServlet(ResponseUtil.getResponseHeader200("show"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {}
}

5、HttpServletRequest 和 HttpServletResponse

HttpServletRequest 和 HttpServletResponse 类分别模拟了HTTP请求和响应对象。HttpServletRequest 包含请求路径和请求方法,HttpServletResponse 包含输出流,用于向客户端发送响应。

package com.qcby.webapps.servlet.req;public class HttpServletRequest {private String path;private String method;public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}

HttpServletRequest 类封装了 HTTP 请求的路径和方法(GET、POST 等)。

package com.qcby.webapps.servlet.req;import java.io.IOException;
import java.io.OutputStream;public class HttpServletResponse {private OutputStream outputStream;public HttpServletResponse(OutputStream outputStream) {this.outputStream = outputStream;}public void writeServlet(String context) throws IOException {outputStream.write(context.getBytes());}
}

HttpServletResponse 类封装了 HTTP 响应,提供了向客户端输出数据的方法。

6. GenericServlet 和 HttpServlet 类

GenericServlet 是一个抽象类,提供了Servlet的基本实现,包括initdestroy方法。HttpServlet 继承自GenericServlet,并实现了service方法,根据请求方法调用相应的doGetdoPost方法。

public abstract class GenericServlet implements Servlet {public void init() {System.out.println("------初始化servlet------");}public void destroy() {System.out.println("------实现servlet对象的销毁------");}
}public abstract class HttpServlet extends GenericServlet {public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {if (request.getMethod().equals("GET")) {doGet(request, response);} else {doPost(request, response);}}protected abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException;protected abstract void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException;
}

7. Servlet 接口

Servlet 接口定义了Servlet的生命周期方法,包括initservicedestroy

package com.qcby.webapps.servlet;import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;import java.io.IOException;public interface Servlet {void init(); // Servlet 初始化void service(HttpServletRequest request, HttpServletResponse response) throws IOException; // 处理请求void destroy(); // 销毁
}

 8. MyTomcat 类

MyTomcat 类是项目的核心,负责启动服务器并处理HTTP请求。它通过ServerSocket监听指定端口,接收客户端请求,解析请求内容,并根据请求路径调用相应的Servlet。

package com.qcby;import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;import static com.qcby.ServletConfigMapping.servletMap;public class MyTomcat {static HttpServletRequest request = new HttpServletRequest();public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8484);while (true) {Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();HttpServletResponse response = new HttpServletResponse(outputStream);int count = 0;while (count == 0) {count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);String context = new String(bytes);System.out.println(context);if (context.equals("")) {System.out.println("你输入了一个空请求");} else {String firstLine = context.split("\\n")[0];request.setPath(firstLine.split("\\s")[1]);request.setMethod(firstLine.split("\\s")[0]);}if (servletMap.containsKey(request.getPath())) {System.out.println("存在于HashMap中");HttpServlet servlet = servletMap.get(request.getPath());servlet.service(request, response);} else {System.out.println("不存在于HashMap中");}}}
}

9. ServletConfigMapping 类

ServletConfigMapping 类维护了URL与Servlet的映射关系。它通过SearchClassUtil扫描指定包下的类,利用反射机制获取带有@WebServlet注解的类,并将其实例化后存入servletMap中。

package com.qcby;import com.qcby.util.SearchClassUtil;
import com.qcby.util.WebServlet;
import com.qcby.webapps.servlet.HttpServlet;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class ServletConfigMapping {public static Map<String, HttpServlet> servletMap = new HashMap<>();static {List<String> classNames = SearchClassUtil.searchClass();for (String path : classNames) {try {Class<?> clazz = Class.forName(path);WebServlet webServlet = clazz.getDeclaredAnnotation(WebServlet.class);HttpServlet servlet = (HttpServlet) clazz.newInstance();servletMap.put(webServlet.urlMapping(), servlet);System.out.println(servletMap);} catch (Exception e) {e.printStackTrace();}}}
}

四、运行流程

  1. 启动 TomcatMyTomcat 类的 main 方法启动,监听 8484 端口。

  2. 接收请求:当有客户端请求到来时,MyTomcat 解析请求的路径和方法。

  3. 分发请求:根据请求路径从 ServletConfigMapping.servletMap 中获取对应的 Servlet 实例,并调用其 service 方法。

  4. 处理请求:Servlet 根据请求方法调用 doGet 或 doPost 方法,生成响应并返回给客户端。

通过手写一个简易版的 Tomcat,我们深入理解了 Servlet 容器的工作原理。虽然这个简易版 Tomcat 功能有限,但它涵盖了 Servlet 容器的核心组件和运行机制。希望本文能帮助你更好地理解 Tomcat 和 Servlet 技术,并为后续深入学习打下坚实的基础。 

相关文章:

手写一个Tomcat

Tomcat 是一个广泛使用的开源 Java Servlet 容器&#xff0c;用于运行 Java Web 应用程序。虽然 Tomcat 本身功能强大且复杂&#xff0c;但通过手写一个简易版的 Tomcat&#xff0c;我们可以更好地理解其核心工作原理。本文将带你一步步实现一个简易版的 Tomcat&#xff0c;并深…...

开发ai模型最佳的系统是Ubuntu还是linux?

在 AI/ML 开发中&#xff0c;​Ubuntu 是更优选的 Linux 发行版&#xff0c;原因如下&#xff1a; ​1. 开箱即用的 AI 工具链支持 Ubuntu 预装了主流的 AI 框架&#xff08;如 TensorFlow、PyTorch&#xff09;和依赖库&#xff0c;且通过 apt 包管理器可快速部署开发环境。 提…...

Scala 中生成一个RDD的方法

在 Scala 中&#xff0c;生成 RDD&#xff08;弹性分布式数据集&#xff09;的主要方法是通过 SparkContext&#xff08;或 SparkSession&#xff09;提供的 API。以下是生成 RDD 的常见方法&#xff1a; 1. 从本地集合创建 RDD 使用 parallelize 方法将本地集合&#xff08;如…...

【redis】慢查询分析与优化

慢查询指在Redis中执行时间超过预设阈值的命令&#xff0c;其日志记录是排查性能瓶颈的核心工具。Redis采用单线程模型&#xff0c;任何耗时操作都可能阻塞后续请求&#xff0c;导致整体性能下降。 命令的执行流程 根据Redis的核心机制&#xff0c;命令执行流程可分为以下步骤…...

P8925 「GMOI R1-T2」Light 题解

P8925 「GMOI R1-T2」Light 让我们好好观察样例解释的这一张图&#xff1a; 左边第 1 1 1 个像到 O O O 点的距离 &#xff1a; L 2 2 L L\times22L L22L 右边第 1 1 1 个像到 O O O 点的距离 &#xff1a; R 2 2 R R\times22R R22R 左边第 2 2 2 个像到 O O O 点…...

Spring Boot + MyBatis + MySQL:快速搭建CRUD应用

一、引言 1. 项目背景与目标 在现代Web开发中&#xff0c;CRUD&#xff08;创建、读取、更新、删除&#xff09;操作是几乎所有应用程序的核心功能。本项目旨在通过Spring Boot、MyBatis和MySQL技术栈&#xff0c;快速搭建一个高效、简洁的CRUD应用。我们将从零开始&#xff…...

python中os库的常用举例

os 库是Python中用于与操作系统进行交互的标准库&#xff0c;以下是一些 os 库的常用示例&#xff1a; 获取当前工作目录 python import os current_dir os.getcwd() print(current_dir) os.getcwd() 函数用于获取当前工作目录的路径。 列出目录内容 python import os …...

Unity 通用UI界面逻辑总结

概述 在游戏开发中&#xff0c;常常会遇到一些通用的界面逻辑&#xff0c;它不论在什么类型的游戏中都会出现。为了避免重复造轮子&#xff0c;本文总结并提供了一些常用UI界面的实现逻辑。希望可以帮助大家快速开发通用界面模块&#xff0c;也可以在次基础上进行扩展修改&…...

Python3 与 VSCode:深度对比分析

Python3 与 VSCode:深度对比分析 引言 Python3 和 Visual Studio Code(VSCode)在软件开发领域扮演着举足轻重的角色。Python3 作为一门强大的编程语言,拥有丰富的库和框架,广泛应用于数据科学、人工智能、网络开发等多个领域。而 VSCode 作为一款轻量级且功能强大的代码…...

第五课:Express框架与RESTful API设计:技术实践与探索

在使用Node.js进行企业应用开发&#xff0c;常用的开发框架Express&#xff0c;其中的中间件、路由配置与参数解析、RESTful API核心技术尤为重要&#xff0c;本文将深入探讨它们在应用开发中的具体使用方法&#xff0c;最后通过Postman来对开发的接口进行测试。 一、Express中…...

Linux 内核自定义协议族开发:从 “No buffer space available“ 错误到解决方案

引言 在 Linux 内核网络协议栈开发中,自定义协议族(Address Family, AF)是实现新型通信协议或扩展内核功能的关键步骤。然而,开发者常因对内核地址族管理机制理解不足,遇到如 insmod: No buffer space available 的错误。本文将以实际案例为基础,深入分析错误根源,并提…...

html-列表标签和表单标签

一、列表标签 表格是用来显示数据的,那么列表就是用来布局的 列表最大的特点就是整齐&#xff64;整洁&#xff64;有序,它作为布局会更加自由和方便&#xff61; 根据使用情景不同,列表可以分为三大类:无序列表&#xff64;有序列表和自定义列表&#xff61; 1.无序列表(重…...

HTML-网页介绍

一、网页 1.什么是网页&#xff1a; 网站是指在因特网上根据一定的规则&#xff0c;使用 HTML 等制作的用于展示特定内容相关的网页集合。 网页是网站中的一“页”&#xff0c;通常是 HTML 格式的文件&#xff0c;它要通过浏览器来阅读。 网页是构成网站的基本元素&#xf…...

动态ip和静态ip适用于哪个场景?有何区别

在数字化浪潮席卷全球的今天&#xff0c;IP地址作为网络世界的“门牌号”&#xff0c;其重要性不言而喻。然而&#xff0c;面对动态IP与静态IP这两种截然不同的IP分配方式&#xff0c;许多用户往往感到困惑&#xff1a;它们究竟有何区别&#xff1f;又分别适用于哪些场景呢&…...

android13打基础: 保存用户免得下次重新登录逻辑

使用SP来做 创建LoginUser.kt // 登录用户需要Email data class LoginUser(val email: String,val password: String, )创建假数据FakeLoginUser.kt object FakeLoginUser {val fake_login_user_items arrayListOf(LoginUser(email "1690544550qq.com",password …...

Linux 4.4 内核源码的目录结构及其主要内容的介绍

以下是 Linux 4.4 内核源码的目录结构及其主要内容的介绍,适用于理解内核模块和驱动开发的基本框架: Linux 4.4 内核源码目录结构 目录作用与内容arch/平台架构相关代码每个子目录对应一种 CPU 架构(如 x86/、arm/、arm64/),包含硬件相关的启动逻辑、中断处理、内存管理等…...

手脑革命:拆解Manus AI如何用“执行智能体”重构生产力——中国团队突破硅谷未竟的技术深水区

第一章&#xff1a;Manus AI 的技术演进与行业背景 1.1 从工具到智能体&#xff1a;AI 技术的范式跃迁 人工智能的发展经历了从规则驱动&#xff08;Rule-based&#xff09;到统计学习&#xff08;Statistical Learning&#xff09;&#xff0c;再到深度学习&#xff08;Deep…...

Android 调用c++报错 exception of type std::bad_alloc: std::bad_alloc

一、报错信息 terminating with uncaught exception of type std::bad_alloc: std::bad_alloc 查了那部分报错c++代码 szGridSize因为文件太大,初始化溢出了 pEGM->pData = new float[szGridSize]; 解决办法 直接抛出异常,文件太大就失败吧 最后还增加一个日志输出,给…...

匿名GitHub链接使用教程(Anonymous GitHub)2025

Anonymous GitHub 1. 引言2. 准备3. 进入Anonymous GitHub官网4. 用GitHub登录匿名GitHub并授权5. 进入个人中心&#xff0c;然后点击• Anonymize Repo实例化6. 输入你的GitHub链接7. 填写匿名链接的基础信息8. 提交9. 实例化对应匿名GitHub链接10. 进入个人中心管理项目11. 查…...

【0基础跟AI学软考高项】成本管理

&#x1f4b0;「成本管理」是什么&#xff1f;‌ ‌一句话解释‌&#xff1a;像家庭装修控制预算&#xff0c;既要买得起好材料&#xff0c;又要避免超支吃泡面——成本管理就是精准算钱、合理花钱、动态盯钱&#xff0c;保证项目不破产&#xff01; &#x1f30b; ‌真实案例…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...