DIY Tomcat:手写一个简易Servlet容器

在Java Web开发领域,Tomcat堪称经典,它作为Servlet容器,承载着无数Web应用的运行。今天,我将带大家一同探索如何手写一个简易的Tomcat,深入理解其底层原理。

一、背景知识

在开始之前,我们需要对几个关键概念有所了解:

  1. Servlet :是一种运行在服务器端的 Java 接口,用于响应客户端请求并生成动态内容。

  2. Servlet 容器 :负责管理 Servlet 的生命周期,包括加载、实例化、调用和销毁等操作。

  3. 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 {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        // 处理 GET 请求的逻辑
    }

    @Override
    public 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{
    @Override
    public void init() {}
    @Override
    public 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/loginhttp://localhost:8080/show 时,Tomcat 会根据请求路径找到对应的 Servlet,并调用其 doGet 或 doPost 方法来处理请求,最终返回相应的响应。

四、总结与展望

通过手写这个简易的 Tomcat,我们深入理解了 Servlet 容器的基本工作原理,包括 Socket 编程、请求解析、Servlet 的加载和调用等关键环节。虽然这个实现功能简单,但它为我们进一步学习和研究 Tomcat 的源码提供了宝贵的实践经验。

在未来的学习中,我们可以继续完善这个简易 Tomcat,添加更多的功能,如支持静态资源的访问、会话管理、过滤器和监听器等,逐步使其功能更加丰富和完善,向真正的 Tomcat 靠拢。

希望这次的分享能为大家打开 Java Web 开发底层世界的大门,让我们一起在技术的海洋中不断探索和前行!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PDF转JPG(并去除多余的白边)

首先&#xff0c;手动下载一个软件&#xff08;poppler for Windows&#xff09;&#xff0c;下载地址&#xff1a;https://github.com/oschwartz10612/poppler-windows/releases/tag/v24.08.0-0 否则会出现以下错误&#xff1a; PDFInfoNotInstalledError: Unable to get pag…

网络安全之端口扫描(一)

前置介绍 什么是DVWA&#xff1f; DVWA&#xff08;Damn Vulnerable Web Application&#xff09;是一个专门设计用于测试和提高Web应用程序安全技能的开源PHP/MySQL Web应用程序。它是一个具有多个安全漏洞的故意不安全的应用程序&#xff0c;供安全专业人员、渗透测试人员、…

财务会计域——合并报表系统设计

摘要 本文主要介绍了合并报表系统的设计&#xff0c;包括其背景、业务流程和系统架构设计。合并报表系统可自动化生成数据&#xff0c;减少人为错误&#xff0c;确保报表合规。其业务流程涵盖数据收集、标准化、合并调整、报表生成、审核及披露等环节。系统架构设计包括数据接…

游戏引擎学习第147天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾 具体来说&#xff0c;我们通过隐式计算来解决问题&#xff0c;而不是像数字微分分析器那样逐步增加数据。我们已经涵盖了这个部分&#xff0c;并计划继续处理音量问题。不过&#xff0c;实际上我们现在不需要继续处理…

NoSQL数据库系统Cassandra学习笔记

详细文档&#xff1a;我用夸克网盘分享了「noSQL.pdf」&#xff0c;点击链接即可保存。打开「夸克APP」在线查看&#xff0c;支持多种文档格式转换。 链接&#xff1a;https://pan.quark.cn/s/dfc3864807b4 参考链接&#xff1a;黑马程序员NoSQL数据库系统Cassandra全套教程&a…

苹果 M3 Ultra 芯片深度解析:AI 时代的性能革命

2025 年 3 月 5 日&#xff0c;苹果正式发布了其史上最强 PC 芯片 ——M3 Ultra。这款基于 UltraFusion 封装技术的旗舰级 SoC&#xff0c;不仅延续了苹果芯片在能效比上的传统优势&#xff0c;更通过架构创新与硬件升级&#xff0c;将 AI 计算能力推向了新高度。本文将从性能突…

Qt从入门到入土(八) -打包Qt程序

前言 当你写完一个有趣的Qt程序时&#xff0c;想发给朋友或者家人&#xff0c;但是他们的电脑又没有安装Qt&#xff0c;那么如何直接在他们电脑上运行又不需要安装Qt呢&#xff1f;本篇文章会告诉你答案&#xff0c;本文详细的介绍了界面设计和功能实现。读完本文你不仅可以学…

使用OpenCV和MediaPipe库——实现人体姿态检测

目录 准备工作如何在Windows系统中安装OpenCV和MediaPipe库&#xff1f; 安装Python 安装OpenCV 安装MediaPipe 验证安装 代码逻辑 整体代码 效果展示 准备工作如何在Windows系统中安装OpenCV和MediaPipe库&#xff1f; 安装Python 可以通过命令行运行python --versio…

React:Axios

axios可以在浏览器和node.js两边跑&#xff0c;可以向服务端发起ajax请求&#xff0c;也可以在node.js里运行&#xff0c;向远端服务发送http请求 Axios中文文档 | Axios中文网 <!DOCTYPE html> <html lang"en"> <head><meta charset"UT…

数据结构第八节:红黑树(初阶)

【本节要点】 红黑树概念红黑树性质红黑树结点定义红黑树结构红黑树插入操作的分析 一、红黑树的概念与性质 1.1 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red和 Black 。 通过对 任何…

使用 vxe-table 导出 excel,支持带数值、货币、图片等带格式导出

使用 vxe-table 导出 excel&#xff0c;支持带数值、货币、图片等带格式导出&#xff0c;通过官方自动的导出插件 plugin-export-xlsx 实现导出功能 查看官网&#xff1a;https://vxetable.cn gitbub&#xff1a;https://github.com/x-extends/vxe-table gitee&#xff1a;htt…

C# Unity 唐老狮 No.7 模拟面试题

本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: 全部 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho 如果你发现了文章内特殊的字体格式,…

【够用就好008】开新坑自学esb32烧录进军物联网和嵌入式

见字如面&#xff0c;这里是AKA AIGC创意人竹相左边。 学习使用了三年的AI工具&#xff0c;现在最大的自信就是业余时间可以学习任何自己感兴趣的事&#xff0c;感觉手搓火箭也不是梦。 今天开个新坑&#xff0c;也是逐步探索想要进入的新世界。物联网&#xff08;IoT&#…

51单片机Proteus仿真速成教程——P1-软件与配置+Proteus绘制51单片机最小系统+新建程序模版

前言&#xff1a;本文主要围绕 51 单片机最小系统的绘制及程序模板创建展开。首先介绍了使用 Proteus 绘制 51 单片机最小系统的详细步骤&#xff0c;包括软件安装获取途径、工程创建、器件添加&#xff08;如单片机 AT89C51、晶振、电容、电阻、按键等&#xff09;、外围电路&…

MacOS Big Sur 11 新机安装brew wget python3.12 exo

MacOS Big Sur 11,算是很老的系统了&#xff0c;所以装起来brew有点费劲。 首先安装brew 官网&#xff1a; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 官网加速&#xff1a; 按照官网的方法&#xff0…

C++算法——差分

1.差分 差分与前缀和的核心思想相同&#xff0c;是预处理&#xff0c;可以在暴力枚举的过程中&#xff0c;快速给出查询的结果&#xff0c;从而优化时间复杂度。 是经典的用空间替换时间的做法。 补充&#xff1a;使得最短跳跃距离尽可能长&#xff0c;遇到类似这样的问题时…

【VBA】WPS/PPT设置标题字体

通过VBA&#xff0c;配合左上角的快速访问工具栏&#xff0c;实现自动化调整 选中文本框的 字体位置、大小、颜色。 配合quicker更加便捷 Sub DisableAutoWrapAndFormat()Dim shp As Shape 检查是否选中了一个形状&#xff08;文本框&#xff09;If ActiveWindow.Selection.Typ…

YOLO 各系列结构整理

目录 2016 You Only Look Once: Unified, Real-Time Object Detection(CVPR) 2017 YOLO9000: Better, Faster, Stronger CVPR 2018 YOLOv3:AnIncrementalImprovemen CVPR YOLO V3-SPP 2020 YOLOv4: Optimal Speed and Accuracy of Object Detection 2021 YOLOV5 2021 YOL…

六十天前端强化训练之第十四天之深入理解JavaScript异步编程

欢迎来到编程星辰海的博客讲解 目录 一、异步编程的本质与必要性 1.1 单线程的JavaScript运行时 1.2 阻塞与非阻塞的微观区别 1.3 异步操作的性能代价 二、事件循环机制深度解析 2.1 浏览器环境的事件循环架构 核心组件详解&#xff1a; 2.2 执行顺序实战分析 2.3 Nod…

Git基础之工作原理

基础概念 git本地有三个工作区域&#xff0c;工作目录 Working Directory&#xff0c;暂存区Stage/Index和资源区Repository/Git Directory&#xff0c;如果在加上远程的git仓库就是四个工作区域 四个区域与文件交换的命令之间的关系 WorkSpace&#xff1a;工作区&#xff0c;就…