手写简易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协议的基础解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30299.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony子系统开发编译构建指导

OpenHarmony子系统开发编译构建指导 概述 OpenHarmony编译子系统是以GN和Ninja构建为基座&#xff0c;对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能扩展的系统&#xff0c;该系统提供以下基本功能&#xff1a; 以部件为最小粒度拼装产品和独…

红日靶场(一)——个人笔记

说明&#xff1a; 红日靶场官网 http://vulnstack.qiyuanxuetang.net/vuln/detail/2/ 靶场默认密码 hongrisec2019 预留空间 【攻击机Kail_2023.3】70G 【靶场win7】32G 【靶场Win2K3 】11G 【靶场winserver08】23G 环境搭建 修改VMnet1、VMnet2网卡的地址 将VMnet1作为内…

网络基础(一)【网络发展/认识协议/网络 VS 系统/以太网通信原理/重谈协议/网络中的地址管理】

网络基础&#xff08;一&#xff09; 1. 网络的发展2. 认识协议3. 网络 VS 系统4. 以太网通信原理5. 重谈协议6. 网络中的地址管理 1. 网络的发展 最开始时&#xff0c;计算机之间相互独立。 但是为了协作完成一些任务&#xff0c;就产生了计算机之间相互通讯的需求&#xff0c…

【ESP-IDF】组件

前言 对于要封装自己的库&#xff0c;在ESP-IDF中&#xff0c;可以采用构建组件的方式导入&#xff0c;而不是单纯在文件夹下导入.h和.c文件&#xff0c;不然一旦要导入的文件过多&#xff0c;它们背后的依赖可能就会相互交叉&#xff0c;不在方便移除和复用。本文就分别讲述&a…

AR配置静态IP双链路负载分担示例

AR配置静态IP双链路负载分担示例 适用于大部分企业网络出口 业务需求&#xff1a; 运营商1分配的接口IP为100.100.1.2&#xff0c;子网掩码为255.255.255.252&#xff0c;网关IP为100.100.1.1。 运营商2分配的接口IP为200.200.1.2&#xff0c;子网掩码为255.255.255.248&am…

视觉 Yolov11 环境配置(GPU版)

虚拟环境 首先用 anaconda 创建虚拟环境 根据自己需求创建一个虚拟环境 一个环境名叫 yolov11-py-3-8 就创建好了&#xff0c;后续的 yolov11 就会以这个环境去做深度学习&#xff08;这里不建议把环境的 py 版本设置到最新&#xff0c;设置个 3.8 或者 3.10 完全够用了 &am…

【微知】如何命令行查看Linux上连接的Wi-Fi信息?(iw dev wlp4s0 link ; information wireless)

背景 有时候没有桌面查看wifi链接&#xff0c;如何命令行查看 命令 iw dev wlp4s0 linkman手册&#xff1a; 实操 综述 如果查看信号强度、协商速率日常使用中比较常用。

从0到1入门Linux

一、常用命令 ls 列出目录内容 cd切换目录mkdir创建新目录rm删除文件或目录cp复制文件或目录mv移动或重命名文件和目录cat查看文件内容grep在文件中查找指定字符串ps查看当前进程状态top查看内存kill终止进程df -h查看磁盘空间存储情况iotop -o直接查看比较高的磁盘读写程序up…

[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(2)实操部署

标题&#xff1a;[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(2)实操部署 水墨不写bug 文章目录 一、无法拷贝类(class uncopyable)的设计解释&#xff1a;重要思想&#xff1a;使用示例 二、锁的RAII设计解释重要考虑使用示例 三、基于RAII模式和互斥锁…

登录固定账号和密码:账号:hmheima

接口文档 【apifox】面试宝典 个人中心-保存用户数据信息 - 教学练测项目-面试宝典-鸿蒙 登录固定账号和密码&#xff1a; 账号&#xff1a;hmheima 密码&#xff1a;Hmheima%123 UI设计稿 【腾讯 CoDesign】面试宝典 CoDesign - 腾讯自研设计协作平台 访问密码&#xff1…

Docker和DockerCompose基础教程及安装教程

Docker的应用场景 Web 应用的自动化打包和发布。自动化测试和持续集成、发布。在服务型环境中部署和调整数据库或其他的后台应用。从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。 CentOS Docker 安装 使用官方安装脚本自动安装 安装命令…

C语言学习笔记-进阶(7)字符串函数3

1. strstr的使用和模拟实现 char * strstr ( const char * str1, const char * str2); Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1. &#xff08;函数返回字符串str2在字符串str1中第⼀次出现的位置&#x…

Elasticsearch为索引设置自动时间戳,ES自动时间戳

文章目录 0、思路1、配置 ingest pipeline2、在索引映射中启用_source字段的时间戳3、使用 index template 全局设置时间戳4、写入测试数据5、验证结果6、总结 在使用 Elasticsearch 进行数据存储和检索时&#xff0c;时间戳字段是一个非常重要的组成部分。它可以帮助我们追踪数…

2024四川大学计算机考研复试上机真题

2024四川大学计算机考研复试上机真题 2024四川大学计算机考研复试机试真题 历年四川大学计算机考研复试机试真题 在线评测&#xff1a;https://app2098.acapp.acwing.com.cn/ 分数求和 题目描述 有一分数序列&#xff1a; 2/1 3/2 5/3 8/5 13/8 21/13… 求出这个数列的前 …

韩国互联网巨头 NAVER 如何借助 StarRocks 实现实时数据洞察

作者&#xff1a; Youngjin Kim Team Leader, NAVER Moweon Lee Data Engineer, NAVER 导读&#xff1a;开源无国界&#xff0c;在“StarRocks 全球用户精选案例”专栏中&#xff0c;我们将介绍韩国互联网巨头 NAVER 的 StarRocks 实践案例。 NAVER 成立于 1999 年&#xff0…

VanillaVueSvelteReactSolidAngularPreact前端框架/库的简要介绍及其优势

VanillaVueSvelteReactSolidAngularPreact前端框架/库的简要介绍及其优势。以下是这些前端框架/库的简要介绍及其优势&#xff1a; 1. Vanilla 定义&#xff1a;Vanilla 并不是一个框架&#xff0c;而是指 原生 JavaScript&#xff08;即不使用任何框架或库&#xff09;。优势…

图像滑块对比功能的开发记录

背景介绍 最近&#xff0c;公司需要开发一款在线图像压缩工具&#xff0c;其中的一个关键功能是让用户直观地比较压缩前后的图像效果。因此&#xff0c;我们设计了一个对比组件&#xff0c;它允许用户通过拖动滑块&#xff0c;动态调整两张图像的显示区域&#xff0c;从而清晰…

从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(十一) 实现服务端和客户端socketio 连接

1.后端部分 socketIO文档参考Socket.IO 首先在lib下新建socket.js文件 参考服务器API | Socket.IO import {Server} from socket.io; import http from http import express from "express"const app express() const server http.createServer(app) const io …

慕慕手记项目日志 项目从开发到部署多环境配置 2025-3-8

慕慕手记项目日志 项目从开发到部署多环境配置 2025-3-8 现在是已经到了课程的第十章了&#xff0c;开始进行配置项目环境了。现在要完成的任务是项目可以正常运行&#xff0c;而且可以自由切换配置&#xff0c;开发/测试。 下面是当前的目录结构图&#xff1a; 现在来解释一…

《Python实战进阶》No15: 数据可视化:Matplotlib 与 Seaborn 的高级用法

No15: 数据可视化&#xff1a;Matplotlib 与 Seaborn 的高级用法 Matplotlib 是 Python 中最受欢迎的数据可视化软件包之一&#xff0c;支持跨平台运行&#xff0c;它是 Python 常用的 2D 绘图库&#xff0c;同时它也提供了一部分 3D 绘图接口。Matplotlib 通常与 NumPy、Pand…