手写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…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
