手写Tomcat:实现基本功能
首先,Tomcat是一个软件,所有的项目都能在Tomcat上加载运行,Tomcat最核心的就是Servlet集合,本身就是HashMap。Tomcat需要支持Servlet,所以有servlet底层的资源:HttpServlet抽象类、HttpRequest和HttpResponse,否则我们无法新建Servlet。
这样我们就可以在webapps写项目了,一个项目有两大资源:servlet资源和静态资源,servlet本身是java类,我们让它调用doGet和doPost方法,必须继承HttpServlet,同时也需要@WebServlet注解,那么在Tomcat中就必须要有@WebServlet注解的实现,如果没有@WebServlet,我们就无法拿到相应的注解。这样我们就能成功搭建起Servlet资源。
如果servlet集合在Tomcat启动之前实现,也就是main()方法之前,需要使用static代码块去实现;如果servlet集合在Tomcat启动之后实现,也就是main()方法之后,再去加载servlet容器。
当Http请求打过来之后,先打到socket上,处理Http请求,其本身就是连接器,从请求上能获取到请求路径和请求方法。先获取到请求路径,判断servlet容器中是否含有相同路径,如果有,再获取请求方法,判断是doGet还是doPost。
一、Servlet资源准备
1、Servlet接口:
- Servlet接口的作用:实现Servlet生命周期,定义init()、service(request,response)和destroy()方法。
- 接口中的方法全都是抽象方法,只定义不实现,方法头默认为public abstract。
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;public interface Servlet {void init();void service(HttpServletRequest request, HttpServletResponse response) throws Exception;void destroy();
}
2、GenericServlet抽象类
- GenericServlet抽象类实现Servlet接口中的init()方法和destroy()方法。
为什么GenericServlet类是抽象类?
我们知道当一个普通类继承一个接口时,需要对接口中的所有方法进行实现,但这里我们不实现Servlet接口中的service(request,response)方法,因此我们需要把GenericServlet类变成抽象类,因为抽象类不必实现接口中的全部方法。
public abstract class GenericServlet implements Servlet {public void init(){System.out.println("初始化成功");}public void destroy(){System.out.println("销毁成功");}
}
3、HttpServlet抽象类
- HttpServlet抽象类实现Servlet接口中的service(request,response)方法
为什么HttpServlet类也是抽象类?
之所以HttpServlet类也是抽象类,是因为普通类中必须对方法进行实现,而在HttpServlet类中,实现service(request,response)方法的逻辑为:如果接收到GET请求,那么执行doGet方法;如果接收到POST请求,那么执行doPost方法。在此我们只需要对doGet和doPost方法进行定义不需要实现,所以HttpServlet只有成为抽象类,才不必实现doGet和doPost方法。
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;public abstract class HttpServlet extends GenericServlet {//实现Servlet生命周期的service方法public void service(HttpServletRequest request, HttpServletResponse response) throws Exception {//如果接收到GET请求,那么执行doGet方法if(request.getMethod().equals("GET")){doGet(request,response);}//如果接收到POST请求,那么执行doPost方法else if(request.getMethod().equals("POST")){doPost(request,response);}}protected abstract void doGet(HttpServletRequest request,HttpServletResponse response) throws Exception;protected abstract void doPost(HttpServletRequest request,HttpServletResponse response)throws Exception;
}
4、HttpRequest类
- HttpRequest类实现请求路径和请求方法的获取与输出。
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;}
}
5、HttpResponse类
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;public class HttpServletResponse {private OutputStream outputStream;public HttpServletResponse(OutputStream outputStream){this.outputStream = outputStream;}public void writeServlet(String context) throws IOException{outputStream.write(context.getBytes());}
}
二、注解
- @Retention():表示该注解的作用阶段。阶段分三种:源代码阶段、类对象阶段和运行时阶段。
- @Target():表示该注解的作用范围。
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 "";//自定义的servlet路径
}
三、工具类
1、SearchClassUtil类
SearchUtil类的功能:扫描com.qcby.webapps包下的文件,获取全路径名
import java.io.File;
import java.util.ArrayList;
import java.util.List;/*
* 扫描com.qcby.webapps包下的文件,获取全路径名
* */
public class SearchClassUtil {public static List<String> classPaths = new ArrayList<String>();public static List<String> searchClass(){//需要扫描的包名String basePack = "com.qcby.webapps";//将获取到的包名转换为路径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);}}
}
2、ResponseUtil类
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;}
}
四、Servlet类
1、LoginServlet类
import com.qcby.Servlet.HttpServlet;
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;
import com.qcby.Util.ResponseUtil;
import com.qcby.Util.WebServlet;import java.io.IOException;@WebServlet(urlMapping = "/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是LoginServlet下的doGet方法");response.writeServlet(ResponseUtil.getResponseHeader200("hello"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {System.out.println("我是LoginServlet下的doPost方法");}
}
五、ServletConfigMapping类
ServletConfigMapping类是Servlet容器,是Tomcat中最核心的部分,其本身是一个HashMap,其功能为:将路径和对象写入Servlet容器中。
import com.qcby.Servlet.HttpServlet;
import com.qcby.Util.SearchClassUtil;
import com.qcby.Util.WebServlet;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class ServletConfigMapping {//创建servlet容器public static Map<String, HttpServlet> servletMap = new HashMap<>();//将Path和对象写入servlet容器当中//采用static代码块 在启动tomcat之前实现static{List<String> classesPath = SearchClassUtil.searchClass();for(String path:classesPath){try{//加载类Class clazz = Class.forName(path);//获取注解WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);HttpServlet servlet = (HttpServlet) clazz.newInstance();servletMap.put(webServlet.urlMapping(),servlet);} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}}
}
六、MyTomcat
import com.qcby.Servlet.HttpServlet;
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class MyTomcat {static HttpServletRequest request = new HttpServletRequest();public static void main(String[] args) throws Exception {//ServletConfigMapping.init(); // tomcat启动,但是还没有监听之前,将信息加载入servlet容器当中//1.创建serversocket对象,持续监听8080端口ServerSocket serverSocket = new ServerSocket(8888);while (true){//accept():阻塞监听 ,当代码执行到这一样,如果没有数据到来,那么循环阻塞在这里。如果有数据到来,就继续向下执行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.setMethod(firstLine.split("\\s")[0]);request.setPath(firstLine.split("\\s")[1]);}//如果我们访问的路径在servlet容器当中存在if(ServletConfigMapping.servletMap.containsKey(request.getPath())){HttpServlet servlet = ServletConfigMapping.servletMap.get(request.getPath());servlet.service(request,response);}}}
}
相关文章:

手写Tomcat:实现基本功能
首先,Tomcat是一个软件,所有的项目都能在Tomcat上加载运行,Tomcat最核心的就是Servlet集合,本身就是HashMap。Tomcat需要支持Servlet,所以有servlet底层的资源:HttpServlet抽象类、HttpRequest和HttpRespon…...
C#变量与变量作用域详解
一、变量基础 1. 声明与初始化 声明语法:<数据类型> <变量名>(如 int age; string name)初始化要求: 1、 类或结构体中的字段变量(全局变量)无需显式初始化,默认值…...

SV学习笔记——数组、队列
一、定宽数组 定宽数组是静态变量,编译时便已经确定其大小,其可以分为压缩定宽数组和非压缩定宽数组:压缩数组是定义在类型后面,名字前面;非压缩数组定义在名字后面。Bit [7:0][3:0] name; bit[7:0] name [3:0]; 1.1定宽数组声明 数组的声…...

API调试工具的无解困境:白名单、动态IP与平台设计问题
引言 你是否曾经在开发中遇到过这样的尴尬情形:你打开了平台的API调试工具,准备一番操作,结果却发现根本无法连接到平台?别急,问题出在调试工具本身。今天我们要吐槽的就是那些神奇的开放平台API调试工具,…...
Git清理本地残留的、但已经在服务器上被删除的分支
要筛选出已经被服务器删除的本地分支,并在本地删除这些分支,可以按照以下步骤进行操作: 步骤 1: 获取远程分支信息,确保本地的远程分支信息是最新的: git fetch -p步骤 2: 列出本地分支和远程分支: git …...
HTTPS实现内容加密的逻辑
加密过程 使用非对称加密,网站生成公钥和私钥浏览器获取到网站公钥(通过验证和解析CA证书),随即生成一串字符串,然后使用公钥加密,发送给网站。网站用私钥将加密内容解析,然后使用这串字符串对…...
使用vue3.0+electron搭建桌面应用并打包exe
使用vue3.0electron搭建桌面应用并打包exe_如何使用electron将vue3vite开发完的项目打包成exe应用程序-CSDN博客...

JSAR 基础 1.2.1 基础概念_空间小程序
JSAR 基础 1.2.1 基础概念_空间小程序 空间空间自由度可嵌入空间空间小程序 最新的技术进展表明,官网之前的文档准备废除了,基于xsml的开发将退出历史舞台,three.js和普通web结合的技术将成为主导。所以后续学习请移步three.js学习路径&#…...

mysql练习
创建数据库db_ck,再创建表t_hero,将四大名著中的主要人物都插入这个表中,将实现过程中sql提交上上来 1、创建数据库db_ck mysql> create database db_ck; 2、创建表t_hero mysql> use db_ck Database changed mysql> create table …...

2025年2月平价旗舰手机性能对比
1、荣耀Magic7 点评:缺席潜望式长焦,3X直立长焦体验还行。兼顾性能、游戏、屏幕、影像、续航、快充等诸多方面,且外围配置比较齐全。 2、vivo x200 点评:潜望式长焦相机,拍照效果好,30W无线充电着实鸡肋&a…...

基于Spring Boot的扶贫助农系统的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
物联网中如何增加其可扩展性 协议 网络 设备 还包括软件层面上的
物联网(IoT)系统的可扩展性是指系统能够随着设备数量、数据流量和业务需求的增长而灵活扩展的能力。为了增加物联网的可扩展性,需要从协议、网络、设备和软件等多个层面进行优化和设计。以下是一些具体的策略和方法: 1. 协议层面的可扩展性 1.1 采用轻量级协议 轻量级协议…...
基于DeepSeek与搜索引擎构建智能搜索摘要工具
基于DeepSeek与搜索引擎构建智能搜索摘要工具 1. 项目概述 本项目通过整合DuckDuckGo搜索引擎与DeepSeek大语言模型,实现了一个智能搜索摘要生成工具。系统可自动执行以下流程: 输入查询语句进行全网搜索获取并解析搜索结果调用AI模型生成结构化摘要输出带来源标注的专业级…...
SQL 简介
SQL 简介 引言 结构化查询语言(Structured Query Language,简称 SQL)是一种用于数据库管理和操作的标准查询语言。它广泛应用于各个领域的数据库管理系统(DBMS)中,用于存储、检索和管理数据。SQL 不仅是数…...

BLUEM2引擎源码2025最新版
BLUE 引擎解析:传奇私服圈中的热门引擎 一、BLUE 引擎简介 BLUE 引擎是传奇私服圈子中较为知名的一款游戏引擎,它在传统的传奇引擎基础上进行了优化和扩展,使得私服开发者可以更加方便地搭建和管理服务器。相比于早期的 GEE、LEG、Hero 等引…...

pytest结合allure
Allure 一、文档二、指令三、装饰器3.1 allure.step装饰器3.2 allure.description装饰器3.3 allure.title装饰器3.4 allure.link、allure.issue 和 allure.testcase装饰器3.5 allure.epic、allure.feature 和 allure.story装饰器3.6 allure.severity装饰器 一、文档 allure文档…...
【渗透测试】基于时间的盲注(Time-Based Blind SQL Injection)
发生ERROR日志告警 查看系统日志如下: java.lang.IllegalArgumentException: Illegal character in query at index 203: https://api.weixin.qq.com/sns/jscode2session?access_token90_Vap5zo5UTJS4jbuvneMkyS1LHwHAgrofaX8bnIfW8EHXA71IRZwsqzJam9bo1m3zRcSrb…...

Gateway:网关路由与登录鉴权
在微服务架构中,用户登录和身份校验的处理方式确实与单体应用有所不同。在单体架构中,一旦用户通过身份验证,其会话信息可以在整个应用范围内共享,所有模块都能访问到用户信息。然而,在微服务架构下,每个服…...

本地部署DeepSeek R1大数据模型知识库
DeepSeek-V3 的综合能力 DeepSeek-V3 在推理速度上相较历史模型有了大幅提升。在目前大模型主流榜单中,DeepSeek-V3 在开源模型中位列榜首,与世界上最先进OpenAI 闭源模型不分伯仲。 1、下载Ollama运行大数据库 Ollama支持 Llama 3.3, DeepSeek-R1, Phi-…...
使用 Python 开发的简单招聘信息采集系统
以下是一个使用 Python 开发的简单招聘信息采集系统,它包含用户登录、招聘信息收集和前后端交互的基本功能。我们将使用 Flask 作为后端框架,HTML 作为前端页面。 项目结构 recruitment_system/ ├── app.py ├── templates/ │ ├── login.html │ ├── index…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...