DIY Tomcat:手写一个简易Servlet容器
在Java Web开发领域,Tomcat堪称经典,它作为Servlet容器,承载着无数Web应用的运行。今天,我将带大家一同探索如何手写一个简易的Tomcat,深入理解其底层原理。
一、背景知识
在开始之前,我们需要对几个关键概念有所了解:
-
Servlet :是一种运行在服务器端的 Java 接口,用于响应客户端请求并生成动态内容。
-
Servlet 容器 :负责管理 Servlet 的生命周期,包括加载、实例化、调用和销毁等操作。
-
Socket 编程 :用于实现网络通信,Tomcat 通过监听 Socket 接收客户端请求。
-

二、核心代码解析
1. MyTomcat.java
这是整个简易 Tomcat 的核心入口。我们创建了一个 ServerSocket 对象,监听 8080 端口,等待客户端的连接。
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {Socket socket = serverSocket.accept();// 处理客户端请求的逻辑
}
当有客户端连接时,通过 Socket 获取输入流,读取请求信息,并解析出请求方法和路径。
InputStream inputStream= socket.getInputStream();HttpServletResponse response=new HttpServletResponse(socket);HttpServletRequest request=new HttpServletRequest();int count=0;while(count==0){count=inputStream.available();}byte[] bytes=new byte[count];inputStream.read(bytes);String context=new String(bytes);
// 解析请求方法和路径
String Context = new String(bytes);
String firstLine = Context.split("\\n")[0];
String method = firstLine.split("\\s")[0];
String path = firstLine.split("\\s")[1];
request.setPath(path);
request.setMethod(method);
根据解析出的路径,在 Servlet 容器中查找对应的 Servlet 对象,并调用其 service 方法。
if(ServletConfigMapping.servletMap.get(request.getPath())!=null)
{System.out.println("------------------");HttpServlet ClassServlet=ServletConfigMapping.servletMap.get(request.getPath());ClassServlet.service(request, response);
}
2. ServletConfigMapping.java
这个类负责初始化 Servlet 容器,扫描指定包下的所有类,获取带有 @WebServlet 注解的类,并将其路径和对象存入 HashMap 中。
public static void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException {List<String> classesPath= SearchClassUtil.searchClass();for(String path:classesPath){getMessage(path);}
}public static void getMessage(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {Class clazz=Class.forName(classPath);WebServlet webServlet=(WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);HttpServlet servlet=(HttpServlet) clazz.newInstance();servletMap.put(webServlet.urlMapping(),servlet);
}
3. LoginServlet.java 和 ShowServlet.java
这两个类是具体的 Servlet 实现,继承自 HttpServlet,并重写 doGet 和 doPost 方法,用于处理不同的 HTTP 请求。
@WebServlet(urlMapping = "/login")
public class LoginServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) {// 处理 GET 请求的逻辑}@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) {// 处理 POST 请求的逻辑}
}
4. SearchClassUtil.java
该工具类用于扫描指定包下的所有类文件,获取其全类名,为 Servlet 容器的初始化提供类路径信息。
public static List<String> searchClass() {String basePack = "com.qcby.webapps";String classPath = SearchClassUtil.class.getResource("/").getPath();try {classPath = URLDecoder.decode(classPath, StandardCharsets.UTF_8.name());} catch (Exception e) {e.printStackTrace();}basePack = basePack.replace(".", File.separator);String searchPath = classPath + basePack;doPath(new File(searchPath), classPath);return classPaths;
}
5. WebServlet.java
这是一个自定义注解,用于标注 Servlet 类,并指定其映射的 URL 路径。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServlet {String urlMapping() default "";
}
6. GenericServlet.java、HttpServlet.java 和 Servlet.java
这些类和接口构成了 Servlet 的基本体系结构。Servlet 接口定义了 Servlet 的生命周期方法,GenericServlet 提供了通用的实现,HttpServlet 则针对 HTTP 请求进行了特殊处理,将请求分发到 doGet 或 doPost 方法。
public interface Servlet {void init();void destroy();void service(HttpServletRequest request, HttpServletResponse response);
}public abstract class GenericServlet implements Servlet{@Overridepublic void init() {}@Overridepublic void destroy() {}
}public abstract class HttpServlet extends GenericServlet {public void service(HttpServletRequest request, HttpServletResponse response){if(request.getMethod().equals("GET")){doGet(request,response);}else if(request.getMethod().equals("POST")){doPost(request,response);}}public abstract void doGet(HttpServletRequest request,HttpServletResponse response);public abstract void doPost(HttpServletRequest request,HttpServletResponse response);
}
7. HttpServletRequest.java 和 HttpServletResponse.java
这两个类用于封装请求和响应信息,方便在 Servlet 中进行操作。
public class HttpServletRequest {private String method;private String path;// getter 和 setter 方法
}public class HttpServletResponse {
}
三、运行效果展示
将上述代码整合后,启动 MyTomcat,它便会开始监听 8080 端口。当我们在浏览器中访问对应的路径,如 http://localhost:8080/login 或 http://localhost:8080/show 时,Tomcat 会根据请求路径找到对应的 Servlet,并调用其 doGet 或 doPost 方法来处理请求,最终返回相应的响应。
四、总结与展望
通过手写这个简易的 Tomcat,我们深入理解了 Servlet 容器的基本工作原理,包括 Socket 编程、请求解析、Servlet 的加载和调用等关键环节。虽然这个实现功能简单,但它为我们进一步学习和研究 Tomcat 的源码提供了宝贵的实践经验。
在未来的学习中,我们可以继续完善这个简易 Tomcat,添加更多的功能,如支持静态资源的访问、会话管理、过滤器和监听器等,逐步使其功能更加丰富和完善,向真正的 Tomcat 靠拢。
希望这次的分享能为大家打开 Java Web 开发底层世界的大门,让我们一起在技术的海洋中不断探索和前行!
相关文章:
DIY Tomcat:手写一个简易Servlet容器
在Java Web开发领域,Tomcat堪称经典,它作为Servlet容器,承载着无数Web应用的运行。今天,我将带大家一同探索如何手写一个简易的Tomcat,深入理解其底层原理。 一、背景知识 在开始之前,我们需要对几个关键…...
如何在Ubuntu上直接编译Apache Doris
以下是在 Ubuntu 22.04 上直接编译 Apache Doris 的完整流程,综合多个版本和环境的最佳实践: 注意:Ubuntu的数据盘VMware默认是20G,编译不够用,给到50G以上吧 一、环境准备 1. 安装系统依赖 # 基础构建工具链 apt i…...
基于ssm的物资进销存(全套)
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本货物进销管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&#…...
【CVPR2025】 EVSSM:用状态空间模型高效去模糊
Efficient Visual State Space Model for Image Deblurring 论文信息 题目: Efficient Visual State Space Model for Image Deblurring 用于图像去模糊的高效视觉状态空间模型 源码:https://github.com/kkkls/EVSSM 创新点 提出了高效视觉状态空间模型…...
动态规划--斐波那契类型
目录 前言 1 第N个斐波那契数 2 爬楼梯 3 三步问题 4 使用最小花费爬楼梯 5 解码方法 总结 前言 本篇所讲的几个题目都是与斐波那契数的做法与思路类似的题目,所以直接放在一块解决了。 同时,由于第一次接触动态规划,我们也会讲解一…...
supervisord管理Gunicorn进程,使用Nginx作为反向代理运行flask web项目
1. 安装 Gunicorn 在项目虚拟环境中安装 Gunicorn:2. 基本用法 配置文件 创建一个 Gunicorn 配置文件(如 gunicorn_config.py),方便管理复杂配置。 示例 gunicorn_config.py: bind "0.0.0.0:8000" #…...
《Python实战进阶》No16: Plotly 交互式图表制作指南
No16: Plotly 交互式图表制作指南 Plotly是一款用来做数据分析和可视化的在线平台,功能真的是非常强大,它主要有以下特点: 图形多样化:在线绘制多种图形,比如柱状图、饼图、直方图、饼图、气泡图、桑基图、股票图、旭…...
代码随想录算法训练营第22天 | 组合总和 分割回文串
39. 组合总和 39. 组合总和 - 力扣(LeetCode) 题目链接/文章讲解:代码随想录 视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_…...
DeepSeek 医疗大模型微调实战讨论版(第一部分)
DeepSeek医疗大模型微调实战指南第一部分 DeepSeek 作为一款具有独特优势的大模型,在医疗领域展现出了巨大的应用潜力。它采用了先进的混合专家架构(MoE),能够根据输入数据的特性选择性激活部分专家,避免了不必要的计算,极大地提高了计算效率和模型精度 。这种架构使得 …...
Linux云计算SRE-第十七周
1. 做三个节点的redis集群。 1、编辑redis节点node0(10.0.0.100)、node1(10.0.0.110)、node2(10.0.0.120)的安装脚本 [rootnode0 ~]# vim install_redis.sh#!/bin/bash # 指定脚本解释器为bashREDIS_VERSIONredis-7.2.7 # 定义Redis的版本号PASSWORD123456 # 设置Redis的访问…...
lvgl在ubuntu中模拟运行
文章目录 前言具体的步骤 前言 lvgl是一个图像UI的开源框架,用于嵌入式的设备之中。 在学习lvgl时,我们最好是现在PC上模拟运行,所以我们学习lvgl的第一步可以说是在我们的电脑上搭建模拟的运行环境。 参考官方的操作 lvgl在ubuntu上模拟运…...
Unity引擎使用HybridCLR(华佗)热更新
大家好,我是阿赵。 阿赵我做手机游戏已经有十几年时间了。记得刚开始从做页游的公司转到去做手游的公司,在面试的时候很重要的一个点,就是会不会用Lua。使用Lua的原因很简单,就是为了热更新。 热更新游戏内容很重要。如果…...
【Linux】权限相关知识点
思考 我们平时使用Linux创建文件或目录时的默认权限是多少? [rootlocalhost test]# mkdir dir [rootlocalhost test]# touch file [rootlocalhost test]# ll total 0 drwxr-xr-x 2 root root 6 Mar 8 15:23 dir #755 -rw-r--r-- 1 root root 0 Mar 8 15:23 f…...
Vue项目通过内嵌iframe访问另一个vue页面,获取token适配后端鉴权(以内嵌若依项目举例)
1. 改造子Vue项目进行适配(ruoyi举例) (1) 在路由文件添加需要被外链的vue页面配置 // 若依项目的话是 router/index.js文件 {path: /contrast,component: () > import(/views/contrast/index),hidden: true },(2) 开放白名单 // 若依项目的话是 permission.js 文件 cons…...
vue3 vite项目安装eslint
npm install eslint -D 安装eslint库 npx eslint --init 初始化配置,按项目实际情况选 自动生成eslint.config.js,可以添加自定义rules 安装ESLint插件 此时打开vue文件就会标红有问题的位置 安装prettier npm install prettier eslint-config-pr…...
Python Flask框架学习汇编
1、入门级: 《Python Flask Web 框架入门》 这篇博文条理清晰,由简入繁,案例丰富,分十五节详细讲解了Flask框架,强烈推荐! 《python的简单web框架flask【附例子】》 讲解的特别清楚,每一步都…...
Excel·VBA江西省预算一体化工资表一键处理
每月制作工资表导出为Excel后都需要调整格式,删除0数据的列、对工资表项目进行排序、打印设置等等,有些单位还分有“行政”、“事业”2个工资表就需要操作2次。显然,这种重复操作的问题,可以使用VBA代码解决 目录 代码使用说明1&a…...
【A2DP】SBC 编解码器互操作性要求详解
目录 一、SBC编解码器互操作性概述 二、编解码器特定信息元素(Codec Specific Information Elements) 2.1 采样频率(Sampling Frequency) 2.2 声道模式(Channel Mode) 2.3 块长度(Block Length) 2.4 子带数量(Subbands) 2.5 分配方法(Allocation Method) 2…...
redis数据类型以及底层数据结构
redis数据类型以及底层数据结构 String:字符串类型,底层就是动态字符串,使用sds数据结构 Map:有两种数据结构:1.压缩列表:当hash结构中存储的元素个数小于了512个。并且元 …...
R软件线性模型与lmer混合效应模型对生态学龙类智力测试数据层级结构应用
全文链接:https://tecdat.cn/?p40925 在生态与生物学研究中,数据常呈现复杂结构特征。例如不同种群、采样点或时间序列的观测数据间往往存在相关性(点击文末“阅读原文”获取完整代码、数据、文档)。 传统线性模型在处理这类非独…...
打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手
DeepSeek AI 聊天助手集成指南 先看完整效果: PixPin_2025-02-19_09-15-59 效果图: 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…...
C语言-语法
数据类型 字符串 C中字符串拼接不用+号,直接使用空格。 char* str = "hello" "world"; 换行链接,加上\就不会报错 char* longStr = "00000000000000000000000000000\ 00000000000000000000000000000"; typedef C 语言提供了 typedef …...
Unity组件TrailRenderer屏幕滑动拖尾
Unity组件TrailRenderer屏幕滑动拖尾 介绍制作总结 介绍 今天要做一个拖动效果,正好用到了TrailRenderer这个组件,正好分享一下 效果参考如下: 制作 1.创建空物体TrailObject添加组件TrailRenderer 下面的材质可以根据自己想要制作的效果去…...
基于昇腾MindIE与GPUStack的大模型容器化部署从入门到入土
引言 昇腾MindIE作为华为面向大模型推理的高性能引擎,结合GPUStack的集群管理能力,能够实现多机多卡的高效资源调度与模型服务化部署。本文将以DeepSeek R1-32B模型为例,详细解析从环境准备到服务验证的全流程实践,涵盖昇腾NPU驱…...
大模型信息整理
1. Benchmarks Reasoning, conversation, Q&A benchmarks HellaSwagBIG-Bench HardSQuADIFEvalMuSRMMLU-PROMT-BenchDomain-specific benchmarks GPQAMedQAPubMedQAMath benchmarks GSM8KMATHMathEvalSecurity-related benchmarks PyRITPurple Llama CyberSecEval2. 国内外…...
【Tools】Windows下Git 2.48安装教程详解
00. 目录 文章目录 00. 目录01. Git简介02. Git参考资料03. Git安装04. Git测试05. 附录 01. Git简介 Git(读音为/gɪt/。)是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。 [1] Git 是 Linus Torvalds 为了帮助管理 Linux 内核…...
【linux网络编程】套接字编程API详细介绍
在C语言中,套接字(Socket)编程主要用于网络通信,尤其是在基于TCP/IP协议的应用程序开发中。常用的套接字编程API主要基于Berkeley Sockets(伯克利套接字)接口,这些函数通常在<sys/socket.h&g…...
护网中shiro常问的问题
1. 漏洞原理 Apache Shiro 是一个强大的 Java 安全框架,提供身份验证、授权、加密及会话管理功能。Shiro 使用 rememberMe 机制来存储用户会话信息,该机制依赖于加密后的 Cookie。当攻击者能够控制 Cookie 并且服务器使用了不安全的反序列化机制时&…...
fastapi房产销售系统
说明: 我希望用fastapi写几个接口,查询房产交易系统的几条数据,然后在postman里面测试 查询客户所有预约记录(含房源信息)需要对应销售经理查询客户所有订单(含房源信息)统计销售经理名下所有房…...
swift -(5) 汇编分析结构体、类的内存布局
一、结构体 在 Swift 标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分 比如Bool、 Int、 Double、 String、 Array、 Dictionary等常见类型都是结构体 ① struct Date { ② var year: Int ③ var month: Int ④ …...
