手写一个Tomcat

ops/2025/3/15 20:30:41/

Tomcat 是一个广泛使用的开源 Java Servlet 容器,用于运行 Java Web 应用程序。虽然 Tomcat 本身功能强大且复杂,但通过手写一个简易版的 Tomcat,我们可以更好地理解其核心工作原理。本文将带你一步步实现一个简易版的 Tomcat,并深入探讨其核心组件和运行机制。

一、项目概述

Tomcat是一个简易的Java Web服务器,它能够处理HTTP请求并调用相应的Servlet进行处理。项目的核心功能包括:

  • 监听HTTP请求并解析请求内容。

  • 根据请求路径调用相应的Servlet。

  • 支持GET和POST请求方法。

  • 使用注解配置Servlet的URL映射。

  • 通过反射机制动态加载Servlet类。

二、项目结构

首先,我们来看一下项目的整体结构:

项目的主要类及其功能如下:

  • ResponseUtil:用于生成HTTP响应头。

  • SearchClassUtil:扫描指定包下的类文件,获取类的全限定名。

  • WebServlet:自定义注解,用于标记Servlet并指定URL映射。

  • LoginServlet 和 ShowServlet:具体的Servlet实现类,处理不同的HTTP请求。

  • HttpServletRequest 和 HttpServletResponse:模拟HTTP请求和响应对象。

  • GenericServlet 和 HttpServlet:抽象类,提供Servlet的基本实现。

  • Servlet:Servlet接口,定义了Servlet的生命周期方法。

  • MyTomcat:主类,负责启动服务器并处理HTTP请求。

  • ServletConfigMapping:维护URL与Servlet的映射关系。

三、核心组件解析

 1、 ResponseUtil 类

ResponseUtil 类用于生成HTTP响应头。它提供了两个静态方法:

  • getResponseHeader200(String context):生成状态码为200的HTTP响应头,并将响应内容附加到响应头后。

  • getResponseHeader404():生成状态码为404的HTTP响应头。

java">public class ResponseUtil {public static final String responseHeader200 = "HTTP/1.1 200 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n";public static String getResponseHeader404() {return "HTTP/1.1 404 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n" + "404";}public static String getResponseHeader200(String context) {return "HTTP/1.1 200 \r\n" +"Content-Type:text/html; charset=utf-8 \r\n" + "\r\n" + context;}
}

 2、SearchClassUtil 类

 SearchClassUtil 类用于扫描指定包下的类文件,并获取这些类的全限定名。它通过递归遍历目录结构,找到所有以.class结尾的文件,并将其路径转换为类的全限定名。

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);return classPaths;}private static void doPath(File file, String classpath) {if (file.isDirectory()) {File[] files = file.listFiles();for (File f1 : files) {doPath(f1, classpath);}} else {if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/", "\\").replaceFirst("\\\\", ""), "").replace("\\", ".").replace(".class", "");classPaths.add(path);}}}
}

 3. WebServlet 注解

WebServlet 是一个自定义注解,用于标记Servlet类并指定URL映射。它包含一个urlMapping属性,用于指定Servlet处理的URL路径。

java">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)
public @interface WebServlet {String urlMapping() default "";
}

  4、LoginServlet 和 ShowServlet 类

 LoginServlet 和 ShowServlet 是两个具体的Servlet实现类,分别处理/login/show路径的请求。它们继承自HttpServlet,并重写了doGet方法以处理GET请求。

java">@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 {}
}@WebServlet(urlMapping = "/show")
public class ShowServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是show");response.writeServlet(ResponseUtil.getResponseHeader200("show"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {}
}

5、HttpServletRequest 和 HttpServletResponse

HttpServletRequest 和 HttpServletResponse 类分别模拟了HTTP请求和响应对象。HttpServletRequest 包含请求路径和请求方法,HttpServletResponse 包含输出流,用于向客户端发送响应。

java">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;}
}

HttpServletRequest 类封装了 HTTP 请求的路径和方法(GET、POST 等)。

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

HttpServletResponse 类封装了 HTTP 响应,提供了向客户端输出数据的方法。

6. GenericServlet 和 HttpServlet 类

GenericServlet 是一个抽象类,提供了Servlet的基本实现,包括initdestroy方法。HttpServlet 继承自GenericServlet,并实现了service方法,根据请求方法调用相应的doGetdoPost方法。

java">public abstract class GenericServlet implements Servlet {public void init() {System.out.println("------初始化servlet------");}public void destroy() {System.out.println("------实现servlet对象的销毁------");}
}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;
}

7. Servlet 接口

Servlet 接口定义了Servlet的生命周期方法,包括initservicedestroy

java">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 {void init(); // Servlet 初始化void service(HttpServletRequest request, HttpServletResponse response) throws IOException; // 处理请求void destroy(); // 销毁
}

 8. MyTomcat 类

MyTomcat 类是项目的核心,负责启动服务器并处理HTTP请求。它通过ServerSocket监听指定端口,接收客户端请求,解析请求内容,并根据请求路径调用相应的Servlet。

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

9. ServletConfigMapping 类

ServletConfigMapping 类维护了URL与Servlet的映射关系。它通过SearchClassUtil扫描指定包下的类,利用反射机制获取带有@WebServlet注解的类,并将其实例化后存入servletMap中。

java">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 = clazz.getDeclaredAnnotation(WebServlet.class);HttpServlet servlet = (HttpServlet) clazz.newInstance();servletMap.put(webServlet.urlMapping(), servlet);System.out.println(servletMap);} catch (Exception e) {e.printStackTrace();}}}
}

四、运行流程

  1. 启动 TomcatMyTomcat 类的 main 方法启动,监听 8484 端口。

  2. 接收请求:当有客户端请求到来时,MyTomcat 解析请求的路径和方法。

  3. 分发请求:根据请求路径从 ServletConfigMapping.servletMap 中获取对应的 Servlet 实例,并调用其 service 方法。

  4. 处理请求:Servlet 根据请求方法调用 doGet 或 doPost 方法,生成响应并返回给客户端。

通过手写一个简易版的 Tomcat,我们深入理解了 Servlet 容器的工作原理。虽然这个简易版 Tomcat 功能有限,但它涵盖了 Servlet 容器的核心组件和运行机制。希望本文能帮助你更好地理解 Tomcat 和 Servlet 技术,并为后续深入学习打下坚实的基础。 


http://www.ppmy.cn/ops/166034.html

相关文章

绝美焦糖暖色调复古风景画面Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 通过 Lr 软件丰富的工具和功能&#xff0c;对风景照片在色彩、影调等方面进行调整。例如利用基本参数调整选项&#xff0c;精准控制照片亮度、对比度、色温、色调等基础要素&#xff1b;运用 HSL 面板可对不同色彩的色相、饱和度以及明亮度进行单独调节&#xff1b;利…

3.angular表单验证

更多用法参考&#xff1a;https://www.jb51.net/article/97552.htm $valid/$invalid // 表单验证通过/表单验证不通过, true/false$pristine/$dirty // 表单验证值是否是初始值 $pristine 初始值, $dirty就是只要改变就为true$error // 验证信息 有错误的才展示里面true 上面…

MySQL Binlog的样式

一、Binlog 的基本概念与记录内容 Binlog 是 MySQL 的二进制日志&#xff0c;以事件形式记录所有对数据库的修改操作&#xff0c;包括&#xff1a; DDL 操作&#xff1a;如 CREATE、ALTER、DROP 等表结构变更。DML 操作&#xff1a;如 INSERT、UPDATE、DELETE 等数据修改。事…

Webpack 打包详细教程

Webpack 是一个现代 JavaScript 应用的静态模块打包工具&#xff0c;它可以处理 JavaScript、CSS、图片等资源&#xff0c;并优化它们以提高性能。以下是 Webpack 从基础到进阶的详细教程。 1. Webpack 基础概念 Webpack 的核心概念包括&#xff1a; Entry&#xff08;入口&a…

基于Asp.net的医院病历管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

泽众TestOne推出快速测试用例设计,让自动化更快捷

在当今快速迭代的软件开发环境中&#xff0c;测试用例设计的效率和质量直接关系到软件交付的速度和质量。传统测试用例设计方法往往依赖于测试人员的经验&#xff0c;经验不足的测试人员可能无法识别潜在的测试场景&#xff0c;导致关键问题被遗漏。同时&#xff0c;传统方法在…

2020年蓝桥杯第十一届CC++大学B组(第一次)真题及代码

目录 1A&#xff1a;跑步训练&#xff08;填空5分_模拟&#xff09; 2B&#xff1a;纪念日&#xff08;填空5分_日期计算&#xff09; 3C&#xff1a;合并检测&#xff08;填空10分_数学&#xff09; 4D&#xff1a;REPEAT程序&#xff08;填空10分_模拟&#xff09; 5E&a…

算法随笔_73: 跳跃游戏

上一篇:算法随笔_72: 最大子数组和-CSDN博客 题目描述如下: 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#x…