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

news2025/3/9 7:20:09

目录

一、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 {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("我是login的doGet方法");
        response.writeServlet(ResponseUtil.getResponseHeader200("hello"));
    }

    @Override
    protected 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 {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // GET请求处理逻辑
        System.out.println("我是show");
        response.writeServlet(ResponseUtil.getResponseHeader200("show"));
    }


    @Override
    protected 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.coloradmin.cn/o/2312019.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mac本地安装运行Redis-单机

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

【ThreeJS Basics 09】Debug

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

【笔记】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; 文章目录 通道通道声明初始化缓冲机制无缓冲通道代码示例 带…

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

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

分布式锁—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 …

[密码学实战]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…

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

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

信息安全与网络安全的区别_信息安全与网络安全之差异探析

在当今数字化时代&#xff0c;信息安全与网络安全成为了人们关注的热点话题。尽管这两个概念经常被提及&#xff0c;但它们之间存在着明显的区别。本文旨在探讨信息安全与网络安全的定义、范畴及应对策略&#xff0c;以帮助读者更好地理解和应对相关挑战。 一、定义与范畴的差…

充电桩快速搭建springcloud(微服务)+前后端分离(vue),客户端实现微信小程序+ios+app使用uniapp(一处编写,处处编译)

充电桩管理系统是专为中小型充电桩运营商、企业和个人开发者设计的一套高效、灵活的管理平台。系统基于Spring Cloud微服务架构开发&#xff0c;采用模块化设计&#xff0c;支持单机部署与集群部署&#xff0c;能够根据业务需求动态扩展。系统前端使用uniapp框架&#xff0c;可…

设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan

前言 这几年芯片设计行业在国内像坐过山车。时而高亢&#xff0c;时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何&#xff1f; 我谈谈自己观点&#xff1a;芯片设计是“劳动密集型”行业。 “EDA和工具高度标准化和代工厂的工艺标准化之后&#xff0c;芯…