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

手写简易Tomcat核心实现:深入理解Servlet容器原理

目录

一、Tomcat概况

1. tomcat全局图

2.项目结构概览

二、实现步骤详解

2.1 基础工具包(com.qcby.util)

2.1.1 ResponseUtil:HTTP响应生成工具

2.1.2 SearchClassUtil:类扫描工具

2.1.3 WebServlet:自定义注解

2.2 Servlet核心实现包(com.qcby.webapps.servlet)

2.2.1 HttpServletRequest:请求对象

2.2.2 HttpServletResponse:响应对象

2.2.3 Servlet接口

2.2.4 GenericServlet抽象类

2.2.5 HttpServlet实现类

2.3 业务Servlet实现(com.qcby.webapps.myweb)

2.3.1 LoginServlet

2.3.2 ShowServlet

2.4 核心控制模块(com.qcby)

2.4.1 ServletConfigMapping:Servlet配置中心

2.4.2 MyTomcat:服务器入口

2.5 运行流程全景图

2.6 关键设计思想

2.6.1 控制反转:

2.6.2 反射机制:

2.6.3 模板方法模式:

2.6.4 职责分离:

三、总结


        本文将基于Java实现一个极简版Tomcat,通过代码逐层剖析Servlet容器的工作原理。读者将掌握HTTP协议解析、注解驱动开发、反射机制、类加载等核心技术。

一、Tomcat概况

1. tomcat全局图

2.项目结构概览

src
├── com.qcby
│   ├── MyTomcat.java          // 服务启动类
│   └── ServletConfigMapping.java // Servlet配置映射
├── com.qcby.util
│   ├── ResponseUtil.java      // HTTP响应工具
│   ├── SearchClassUtil.java   // 类扫描工具
│   └── WebServlet.java        // 自定义注解
└── com.qcby.webapps├── myweb                  // 示例Servlet└── servlet                // Servlet核心实现

二、实现步骤详解

2.1 基础工具包(com.qcby.util)

2.1.1 ResponseUtil:HTTP响应生成工具

package com.qcby.util;public class ResponseUtil {// 200响应头模板public static final String responseHeader200 = "HTTP/1.1 200 \r\nContent-Type:text/html; charset=utf-8 \r\n\r\n";// 生成404响应public static String getResponseHeader404(){return "HTTP/1.1 404 \r\nContent-Type:text/html; charset=utf-8 \r\n\r\n404";}// 生成带内容的200响应public static String getResponseHeader200(String context){return responseHeader200 + context;}
}

核心作用

  • 标准化HTTP响应格式

  • 提供快速构建响应的静态方法

  • 支持200/404状态码生成

2.1.2 SearchClassUtil:类扫描工具

package com.qcby.util;import java.io.File;
import java.util.ArrayList;
import java.util.List;/**'* 扫描com.qcby.webapps目录下面的文件,获取每一个Java文件的全类名*/
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);//这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象return classPaths;}/*** 该方法会得到所有的类,将类的绝对路径写入到classPaths中* @param file*/private static void doPath(File file,String classpath) {if (file.isDirectory()) {//文件夹//文件夹我们就递归File[] files = file.listFiles();for (File f1 : files) {doPath(f1,classpath);}} else {//标准文件//标准文件我们就判断是否是class文件if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");//如果是class文件我们就放入我们的集合中。classPaths.add(path);}}}public static void main(String[] args) {List<String> classes = SearchClassUtil.searchClass();for (String s: classes) {System.out.println(s);}}
}

核心作用

  • 递归扫描指定包路径

  • 转换.class文件为全限定类名

  • 支持动态类加载的基础

2.1.3 WebServlet:自定义注解

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)//表明@WebSerlet注解是写在类上
public @interface WebServlet {//String path=null;String urlMapping() default "" ;
}

核心作用

  • 替代XML配置的注解式声明

  • 建立Servlet与URL的映射关系

  • 运行时(RUNTIME)保留策略支持反射读取可读取注解信息

2.2 Servlet核心实现包(com.qcby.webapps.servlet)

2.2.1 HttpServletRequest:请求对象

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;}
}
  • 职责分离:请求封装客户端数据,响应处理输出流

2.2.2 HttpServletResponse:响应对象

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());}}

2.2.3 Servlet接口

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<response> {public void init();public void service(HttpServletRequest request, HttpServletResponse response) throws IOException;public void destroy();}

2.2.4 GenericServlet抽象类

package com.qcby.webapps.servlet;public abstract  class GenericServlet implements  Servlet {public void init(){System.out.println("------初始化servlet------");}public void destroy(){System.out.println("------实现servlet对象的销毁------");}}

2.2.5 HttpServlet实现类

package com.qcby.webapps.servlet;import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;import java.io.IOException;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;}
  • 设计模式:模板方法模式处理HTTP方法分发

  • 继承体系:HttpServlet -> GenericServlet -> Servlet

核心作用

  • 构建完整的Servlet生命周期

  • 实现HTTP方法的分发机制

  • 提供请求响应的标准接口

2.3 业务Servlet实现(com.qcby.webapps.myweb)

2.3.1 LoginServlet

package com.qcby.webapps.myweb;import com.qcby.util.ResponseUtil;
import com.qcby.util.WebServlet;
import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;import java.io.IOException;
@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 {}
}

2.3.2 ShowServlet

package com.qcby.webapps.myweb;import com.qcby.util.ResponseUtil;
import com.qcby.util.WebServlet;import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;import java.io.IOException;@WebServlet(urlMapping="/show")
public class ShowServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// GET请求处理逻辑System.out.println("我是show");response.writeServlet(ResponseUtil.getResponseHeader200("show"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {// POST请求处理逻辑}
}

核心作用

  • 演示具体业务实现

  • 展示注解驱动的路由配置

  • 体现模板方法模式的应用

2.4 核心控制模块(com.qcby)

2.4.1 ServletConfigMapping:Servlet配置中心

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 =(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. 静态初始化块在类加载时执行

  2. 使用SearchClassUtil扫描所有类

  3. 通过反射获取注解信息

  4. 实例化Servlet并建立URL映射

2.4.2 MyTomcat:服务器入口

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(8081);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=ServletConfigMapping.servletMap.get(request.getPath());servlet.service(request,response);} else {System.out.println("不存在于HashMap中");}}}}

核心流程

  1. 创建ServerSocket监听8081端口

  2. 循环接受客户端连接

  3. 解析HTTP请求首行

  4. 根据路径查找对应的Servlet

  5. 调用service方法处理请求

  6. 返回404响应处理异常路径

2.5 运行流程全景图

启动流程:
1. MyTomcat.main()启动
2. ServletConfigMapping静态块初始化→ SearchClassUtil扫描类→ 反射创建Servlet实例→ 建立URL映射表请求处理流程:
客户端请求 → ServerSocket接收 → 解析请求路径 → 
查找Servlet映射 → 调用service() → 执行doGet/doPost → 
生成响应 → 返回客户端

2.6 关键设计思想

2.6.1 控制反转

  • Servlet实例由容器创建
  • 开发者通过注解声明路由

2.6.2 反射机制

  • 动态加载类文件
  • 读取注解配置信息

2.6.3 模板方法模式

  • HttpServlet定义处理骨架
  • 子类实现具体业务逻辑

2.6.4 职责分离

  • Request/Response对象封装协议细节
  • Util类处理通用逻辑

三、总结

通过实现这个简易Tomcat,我们深入理解了:

  1. Servlet容器的启动流程

  2. 请求-响应生命周期管理

  3. 注解驱动与反射的应用

  4. HTTP协议的基础解析

相关文章:

手写简易Tomcat核心实现:深入理解Servlet容器原理

目录 一、Tomcat概况 1. tomcat全局图 2.项目结构概览 二、实现步骤详解 2.1 基础工具包&#xff08;com.qcby.util&#xff09; 2.1.1 ResponseUtil&#xff1a;HTTP响应生成工具 2.1.2 SearchClassUtil&#xff1a;类扫描工具 2.1.3 WebServlet&#xff1a;自定义注解…...

ES Filter Query 区别

在 Elasticsearch&#xff08;ES&#xff09; 中&#xff0c;Filter 和 Query 是两种常用的数据检索方式&#xff0c;它们的主要区别在于 是否计算相关性分数&#xff08;Score&#xff09; 以及 是否使用缓存。以下是它们的详细区别和应用场景&#xff1a; 1. 核心区别 特性F…...

Java多线程与高并发专题——关于CopyOnWrite 容器特点

引入 在 CopyOnWriteArrayList 出现之前&#xff0c;我们已经有了 ArrayList 和 LinkedList 作为 List 的数组和链表的实现&#xff0c;而且也有了线程安全的 Vector 和Collections.synchronizedList() 可以使用。 首先我们来看看Vector是如何实现线程安全的 &#xff0c;还是…...

春节面对大流量并发,系统该如何设计

基于6个层次进行系统设计以解决大流量瞬时并发问题&#xff1a;CDN层&#xff0c;Nginx层&#xff0c;服务层&#xff0c;缓存层&#xff0c;数据库层&#xff0c;全链路压测监控。以下为具体设计参考 1. CDN&#xff08;内容分发网络&#xff09; 作用&#xff1a;静态资源&…...

mac本地安装运行Redis-单机

记录一下我以前用的连接服务器的跨平台SSH客户端。 因为还要准备毕设...... 服务器又过期了&#xff0c;只能把redis安装下载到本地了。 目录 1.github下载Redis 2.安装homebrew 3.更新GCC 4.自行安装Redis 5.通过 Homebrew 安装 Redis 安装地址&#xff1a;https://git…...

77.ObservableCollection使用介绍1 C#例子 WPF例子

可观察集合ObservableCollection using System; using System.Collections.ObjectModel;class Program {static void Main(){// 创建一个可观察集合ObservableCollection<string> list new ObservableCollection<string>();// 注册集合变化事件list.CollectionCh…...

【ThreeJS Basics 09】Debug

文章目录 简介从 dat.GUI 到 lil-gui例子安装 lil-gui 并实例化不同类型的调整改变位置针对非属性的调整复选框颜色 功能/按钮调整几何形状文件夹调整 GUI宽度标题关闭文件夹隐藏按键切换 结论 简介 每一个创意项目的一个基本方面是能够轻松调整。开发人员和参与项目的其他参与…...

在 k8s中查看最大 CPU 和内存的极限

在 Kubernetes&#xff08;k8s&#xff09;中&#xff0c;你可以从不同层面查看最大 CPU 和内存的极限&#xff0c;下面为你详细介绍从节点和集群层面查看的方法。 查看节点的 CPU 和内存极限 节点的 CPU 和内存极限是指单个节点上可分配的最大资源量&#xff0c;可通过以下几…...

【笔记】STM32L4系列使用RT-Thread Studio电源管理组件(PM框架)实现低功耗

硬件平台&#xff1a;STM32L431RCT6 RT-Thread版本&#xff1a;4.1.0 目录 一.新建工程 二.配置工程 ​编辑 三.移植pm驱动 四.配置cubeMX 五.修改驱动文件&#xff0c;干掉报错 六.增加用户低功耗逻辑 1.设置唤醒方式 2.设置睡眠时以及唤醒后动作 ​编辑 3.增加测试命…...

类和对象:

1. 类的定义&#xff1a; 1. 类定义格式&#xff1a; 对于我们的类的话&#xff0c;我们是把类看成一个整体&#xff0c;我们的函数里面没有找到我们的成员变量&#xff0c;我们就在我们的类里面找。 我们看我们的第二点&#xff1a; 我们的类里面&#xff0c;我们通常会对…...

【十三】Golang 通道

&#x1f4a2;欢迎来到张胤尘的开源技术站 &#x1f4a5;开源如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 通道通道声明初始化缓冲机制无缓冲通道代码示例 带…...

对接RAGflow的API接口报错

对接RAGflow的API接口&#xff0c;报错&#xff1a; {"status":"success","message":"API连接正常","response":{"code":109,"data":false,"message":"Authentication error: API key …...

软考中级_【软件设计师】知识点之【面向对象】

简介&#xff1a; 软件设计师考试中&#xff0c;面向对象模块为核心考点&#xff0c;涵盖类与对象、继承、封装、多态等基础概念&#xff0c;重点考查UML建模&#xff08;类图/时序图/用例图&#xff09;、设计模式&#xff08;如工厂、单例模式&#xff09;及SOLID设计原则。要…...

Excel中COUNTIF用法解析

COUNTIF 是 Excel 中一个非常实用的函数&#xff0c;用于统计满足某个条件的单元格数量。它的基本语法如下&#xff1a; 基本语法 COUNTIF(范围, 条件) 范围&#xff1a;需要统计的单元格区域&#xff0c;例如 A1:A10 或整列 A:A。 条件&#xff1a;用于判断哪些单元格需要被…...

分布式锁—7.Curator的分布式锁一

大纲 1.Curator的可重入锁的源码 2.Curator的非可重入锁的源码 3.Curator的可重入读写锁的源码 4.Curator的MultiLock源码 5.Curator的Semaphore源码 1.Curator的可重入锁的源码 (1)InterProcessMutex获取分布式锁 (2)InterProcessMutex的初始化 (3)InterProcessMutex.…...

《UE5_C++多人TPS完整教程》学习笔记35 ——《P36 武器类(Weapon Class)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P36 武器类&#xff08;Weapon Class&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephen …...

【SpringMVC】SpringMVC的启动过程与原理分析:从源码到实战

SpringMVC的启动过程与原理分析&#xff1a;从源码到实战 SpringMVC是Spring框架中用于构建Web应用的核心模块&#xff0c;它基于MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;提供了灵活且强大的Web开发能力。本文将深入分析SpringMVC的启动过程、核…...

出现“ping不通但可以远程连接”的情况可能由以下原因导致

出现“ping不通但可以远程连接”的情况可能由以下原因导致&#xff1a; 1.防火墙或安全软件限制 • 原因&#xff1a;防火墙或安全软件可能阻止了ICMP数据包&#xff08;ping使用的协议&#xff09;&#xff0c;但允许了远程连接所需的协议&#xff08;如TCP&#xff09;。 …...

MySQL表空间碎片原理和解决方案

一、表空间与碎片的基本概念 表空间&#xff1a;MySQL中存储表数据和索引的物理文件&#xff08;如InnoDB的.ibd文件&#xff09;。分为系统表空间和独立表空间。碎片&#xff1a;数据在物理存储上不连续&#xff0c;分为行级碎片&#xff08;单行跨多页&#xff09;和页级碎片…...

[密码学实战]Java实现国密TLSv1.3单向认证

一、代码运行结果 1.1 运行环境 1.2 运行结果 1.3 项目架构 二、TLS 协议基础与国密背景 2.1 TLS 协议的核心作用 TLS(Transport Layer Security) 是保障网络通信安全的加密协议,位于 TCP/IP 协议栈的应用层和传输层之间,提供: • 数据机密性:通过对称加密算法(如 AE…...

最小栈 _ _

一&#xff1a;题目 二&#xff1a;思路 解释&#xff1a;一个栈名为st&#xff0c;其用来正常的出入栈&#xff0c;一个栈名为minst&#xff0c;其的栈顶元素一定是最小的元素 入栈&#xff1a;第一个元素&#xff0c;两个栈一起入&#xff0c;后面再入栈&#xff0c;只有入栈…...

HTTPS加密原理详解

目录 HTTPS是什么 加密是什么 HTTPS的工作流程 1.使用对称加密 2.引入非对称加密 3.引入证书机制 客户端验证证书真伪的过程 签名的加密流程 整体工作流程 总结 HTTPS是什么 HTTPS协议也是一个应用程协议&#xff0c;是在HTTP的基础上加入了一个加密层&#xff0c;由…...

黑金风格人像静物户外旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 针对人像、静物以及户外旅拍照片&#xff0c;运用 Lightroom 软件进行风格化调色工作。旨在通过软件中的多种工具&#xff0c;如基本参数调整、HSL&#xff08;色相、饱和度、明亮度&#xff09;调整、曲线工具等改变照片原本的色彩、明度、对比度等属性&#xff0c;将…...

安装pyqt6出现的问题

安装PyQt6报错&#xff1a; PermissionError: [WinError 32] 另一个程序正在使用此文件&#xff0c;进程无法访问。: C:\\Users\\xyj19\\AppData\\Local\\Temp\\tmp3xfmekh7 [end of output] note: This error originates from a subprocess, and is likely not a pr…...

java调用c++

VScode 配置java 并且使用JNA调用c 动态库 安装 Java 开发环境 ​ 安装 JDK官网直接下载就好&#xff0c;推荐镜像下载 通过网盘分享的文件&#xff1a;jdk-8u144-windows-x64.exe​ 链接: https://pan.baidu.com/s/1Ov9bJkPNnOgcliBL-PSTFQ?pwdpg43 提取码: pg43 ​ 直接安…...

gitlab+jenkins+harbor+k8s安装操作流程之Jenkins

准备环境 一台centos7系统 4C/8G/100G 如果是jenkins2.5以上版本需要centos8以上版本 JDK1.8编译安装(最新版本jdk需要18以上) MAVEN编译安装 GIT编译安装 JDK1.8步骤 tar -zxvf 解压 vim /etc/profile export JAVA_HOME/data/jdk1.8.0_111 export JRE_HOME$JAVA…...

【机械视觉】C#+VisionPro联合编程———【三、加载CogToolBlock工具详解,以及实例】

【机械视觉】C#VisionPro联合编程———【三、加载CogToolBlock工具详解&#xff0c;以及实例】 在VisionPro中&#xff0c;CogToolBlock 是一种容器工具&#xff0c;可以将多个视觉工具&#xff08;如CogBlob、CogPMAlign等&#xff09;组合成一个可复用的流程。通过C#与Visi…...

Android14 串口控制是能wifi adb实现简介

Android14 串口控制是能wifi adb实现简介 一、前言 文章目录 Android14 串口控制是能wifi adb实现简介一、前言二、Android14 串口控制是能wifi adb实现1、设置prop属性命令开启adb&#xff08;1&#xff09;相关prop属性设置&#xff08;2&#xff09;在设置界面或者 ifconfi…...

启动wsl里的Ubuntu24报错:当前计算机配置不支持 WSL2,HCS_E_HYPERV_NOT_INSTALLED

问题&#xff1a;启动wsl里的Ubuntu24报错 报错信息&#xff1a; 当前计算机配置不支持 WSL2。 请启用“虚拟机平台”可选组件&#xff0c;并确保在 BIOS 中启用虚拟化。 通过运行以下命令启用“虚拟机平台”: wsl.exe --install --no-distribution 有关信息&#xff0c;请访…...

常用AI工具推荐

AI对话相关 Deepseek https://chat.deepseek.com/ Kimi https://kimi.moonshot.cn/ 豆包 https://www.doubao.com/ 文心一言 https://yiyan.baidu.com/ 腾讯元宝 https://yuanbao.tencent.com 通义千问 https://tongyi.aliyun.com/qianwen/ 图片生成相关 即梦 https://jimen…...