手写tomcat(Ⅲ)——tomcat动态资源的获取
仿写tomcat的Servlet接口体系
之前写过一篇博客,Tomcat的Servlet-GenericServlet-HttpServlet体系的具体结构,以及Servlet的生命周期
Servlet讲解
想要模仿tomcat获取动态资源,就需要我们自己仿写一个Servlet接口体系
主要包括:注解,相关的接口+子类,搜索包名下的所有servlet的工具类
MyWebServlet注解
我们首先需要定义一个MyWebServlet注解,仿照了WebServlet注解
import java.lang.annotation.*;@Target({ElementType.TYPE})// 作用到类
@Retention(RetentionPolicy.RUNTIME)// 运行阶段
public @interface MyWebServlet {// 定义url变量,并规定默认值为空字符String className() default "";String url() default "";
}
相关的接口+子类
MyServlet接口,定义了Servlet的生命周期
import java.io.IOException;import com.qcby.config.MyServletConfig;// 自定义的servlet接口
public interface MyServlet {void init();MyServletConfig getServletConfig();String getServletInfo();void service(Request request, Response response) throws IOException;void destroy();
}
自定义的MyServletConfig类
/*** 将获取到的url和classPath径封装到该对象当中*/
public class MyServletConfig {private String url;private String classPath;public MyServletConfig(String url, String classPath) {this.url = url;this.classPath = classPath;}public MyServletConfig() {}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getClassPath() {return classPath;}public void setClassPath(String classPath) {this.classPath = classPath;}
}
抽象类MyGenericServlet实现了除service以外的方法
import com.qcby.config.MyServletConfig;public abstract class MyGenericServlet implements MyServlet {@Overridepublic void init() {}@Overridepublic MyServletConfig getServletConfig() {return new MyServletConfig();}@Overridepublic String getServletInfo() {return "";}@Overridepublic void destroy() {}
}
抽象类MyHttpServlet,实现了service方法,并将其分化为doGet,doPost,doDelete等方法
import java.io.IOException;public abstract class MyHttpServlet extends MyGenericServlet {// 重写service方法,将其分化为不同的方法// 抽象类MyHttpServlet没有抽象方法@Overridepublic void service(Request request, Response response) throws IOException {// 判断request的methodType,转发到doGet/doPost当中if ("GET".equals(request.getMethodType())) {myDoGet(request, response);} else if ("POST".equals(request.getMethodType())) {myDoPost(request, response);}}public void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {}public void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {}}
这里的MyHttpServletRequest还有MyHttpServletResponse,是两个接口
public interface MyHttpServletRequest {
}
public interface MyHttpServletResponse {void write(String context) throws IOException;
}
分别由Request类和Response类实现
public class Request implements MyHttpServletRequest{private String url;private String methodType;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getMethodType() {return methodType;}public void setMethodType(String methodType) {this.methodType = methodType;}
}
/*** 做数据的返回*/
public class Response implements MyHttpServletResponse {// 获取输出流private OutputStream outputStream;public Response(OutputStream outputStream) {this.outputStream = outputStream;}// 静态资源的输出public void writeHtml(String path) throws Exception {//// 根据路径返回资源路径地址,例如http://localhost:8666/index.htmlString resource = FileUtil.getResoucePath(path);File file = new File(resource);if (file.exists()) {// 静态资源存在!System.out.println("静态资源存在!");FileUtil.writeFile(file, outputStream);} else {System.out.println(path + "对应的该静态资源不存在!");}}// 数据写回public void write(String context) throws IOException {outputStream.write(context.getBytes());outputStream.flush();}
}
加载servlet包下的所有servlet,并将其MyWebServlet注解的url值和对应的servlet类对象组装成一个map(最重要)
上面基本都是照猫画葫芦的仿写,其实该实现的方法完全没实现,跟原本的tomcat相比差的比较多
但是这一部分,还原度比较高,tomcat本身也是扫描包,并且反射获取注解的取值,最后组成HashMap来映射servlet的
SearchClassUtil
/*** 扫描指定包,获取该包下所有的类的全路径信息*/
public class SearchClassUtil {public static List<String> classPaths = new ArrayList<String>();public static List<String> searchClass(){//需要扫描的包名String basePack = "com.qcby.webapp";//将获取到的包名转换为路径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);}}
}
tomcat启动类
对静态资源和动态资源做了判断
package com.qcby.tomcatDemo;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLDecoder;import com.qcby.config.ServletConfigMapping;
import com.qcby.servlet.MyServlet;
import com.qcby.servlet.Request;
import com.qcby.servlet.Response;
import com.qcby.util.ResponseUtil;/*** tomcat启动类*/
public class TomcatStart {private static Request request = new Request();public static void main(String[] args) throws IOException, ClassNotFoundException {System.out.println("socket服务器启动!!!");// 0. 加载servlet类信息ServletConfigMapping.init();// 1. 打开相关通信端口// tomcat:8080,mysql:3306,应用软件独占一个端口的全部信息ServerSocket serverSocket = new ServerSocket(8666);// 线程持续扫描当前网卡xxxx端口(死循环),如果有数据就拿过来,交给端口对应的程序处理// 2. 监听并接收请求数据while (true) {// 一旦发现有数据,就打开socket通信// 这里没有创建新的线程,所以这里是main线程监听数据Socket socket = serverSocket.accept();System.out.println(socket.getInetAddress().getCanonicalHostName() + "进行了连接!");// 第二步监听并接收到了数据,处理数据可以用主线程,但是没必要,创建子线程处理// 每接收一次数据,创建一个子线程Thread t1 = new Thread(() -> {// 处理数据包括两部分:读和写try {dataHandle(socket);} catch (Exception e) {throw new RuntimeException(e);}});t1.start();}}// 处理数据的方法,读+写public static void dataHandle(Socket socket) throws Exception {// 1. 读取请求的数据// 1.1打开输入流对象,读取socket对象中的数据,这里的数据都是0101010的二进制数据InputStream inputStream = socket.getInputStream();requestContext(inputStream);// 数据的输出Response response = new Response(socket.getOutputStream());if ("".equals(request.getUrl())) {response.write(ResponseUtil._404ResponseHeader);} else if (ServletConfigMapping.classMap.get(request.getUrl()) != null) {// 动态web资源dispatch(request, response);} else {// 访问静态资源response.writeHtml(request.getUrl());}}public static void dispatch(Request request, Response response) throws InstantiationException, IllegalAccessException, IOException {Class<MyServlet> servletClass = ServletConfigMapping.classMap.get(request.getUrl());if (servletClass != null) {MyServlet myServlet = servletClass.newInstance();myServlet.service(request, response);}}public static void requestContext(InputStream inputStream) throws IOException {// 1.2 二进制数据的翻译并读取int count = 0;while (count == 0) {// 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。// 下一个调用可能是同一个线程,也可能是另一个线程。// 一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。// 可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回 0count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);// 这里用URLDecoder是为了防止路径中出现特殊符号,经过get请求之后会被URLEncode为乱码String context = URLDecoder.decode(new String(bytes, "utf-8"));// Base64.getDecoder().decode(bytes)System.out.println("===context:" + context);if ("".equals(context)) {System.out.println("null request!");request.setUrl("");request.setMethodType("");} else {//根据换行来获取第一行数据String firstLine = context.split("\\n")[0];// 第一行数据的第2个字符串System.out.println("===url:" + firstLine.split("\\s")[1]);request.setUrl(firstLine.split("\\s")[1]);// 第一行数据的第1个字符串System.out.println("===methodType:" + firstLine.split("\\s")[0]);request.setMethodType(firstLine.split("\\s")[0]);}}
}
webapp包下有两个servlet
@MyWebServlet(url = "/first")
public class FirstServlet extends MyHttpServlet {@Overridepublic void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {String context = "<h1>这是FirstServlet的myDoGet方法</h1>";resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));}@Overridepublic void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {String context = "<h1>这是FirstServlet的myDoPost方法</h1>";resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));}
}
@MyWebServlet(url = "/hello")
public class HelloServlet extends MyHttpServlet {@Overridepublic void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {String context = "<h1>这是HelloServlet的myDoGet方法</h1>";resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));}@Overridepublic void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {String context = "<h1>这是HelloServlet的myDoPost方法</h1>";resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));}
}
项目下还有一个静态资源index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><p>Hello TomcatDemo!!!</p>
</body>
</html>
我们在浏览器分别测试静态和动态资源的获取
相关文章:

手写tomcat(Ⅲ)——tomcat动态资源的获取
仿写tomcat的Servlet接口体系 之前写过一篇博客,Tomcat的Servlet-GenericServlet-HttpServlet体系的具体结构,以及Servlet的生命周期 Servlet讲解 想要模仿tomcat获取动态资源,就需要我们自己仿写一个Servlet接口体系 主要包括:…...
软件测试面试题(四)
一:测试评估的目标? 量化测试进程 生成缺陷和测试覆盖率的总结报告 测试评估的问题 没有把测试覆盖率作为报告测试进程的根据,使得不知测试是否结束; 没有做测试缺陷评估,缺陷评估是量度软件可行性的重要指标&…...
infoq学习笔记-云原生网关当道,三大主流厂商如何“竞 技”?
注基础组件的质量,这些基础组件是用户看不到的。这些组件包括代码质量、自动化的CI/CD、端对端测试、混沌测试等。在APISIX中,我们内置了大 量的测试案例代码,包括单元测试、E2E测试、混沌测试,以及一些基准测试等,从而…...

Python中别再用 ‘+‘ 拼接字符串了!
大家好,在 Python 编程中,我们常常需要对字符串进行拼接。你可能会自然地想到用 操作符将字符串连接起来,毕竟这看起来简单明了。 在 Python 中,字符串是不可变的数据类型,这意味着一旦字符串被创建,它就…...
前端上传heic图片转jpe格式并展示
各大浏览器对 HEIC 格式图片的支持情况,包括上传和显示的支持度 浏览器版本HEIC 上传HEIC 显示Chrome版本 85 及以上支持不支持Firefox所有版本支持不支持Safari版本 11 及以上支持支持Edge版本 18 及以上支持不支持Opera所有版本支持不支持IE不支持不支持不支持 …...

VMware虚拟机-设置系统网络IP、快照、克隆
1.设置网络IP 1.点击右上角开关按钮-》有线 已连接-》有线设置 2.手动修改ip 3.重启或者把开关重新关闭开启 2.快照设置 快照介绍: 通过快照可快速保存虚拟机当前的状态,后续可以使用虚拟机还原到某个快照的状态。 1.添加快照(需要先关闭虚拟机) 2.在…...

指纹识别概念解析
目录 1. 指纹是物证之首 1.1 起源于中国 1.2 发展于欧洲 1.3 流行于全世界 2. 指纹图像 3. 指纹特征 4. 指纹注册 5. 指纹验证 6. 指纹辨识 1. 指纹是物证之首 指纹识别技术起源于中国、发展于欧洲、流行于全世界。自20世纪以来,指纹在侦破刑事案件、解决诉…...
图像处理神经网络数据预处理步骤的详细解释和分析
1. 尺寸调整(Resizing) 目的:神经网络通常需要固定尺寸的输入图像。通过统一图像尺寸,可以确保输入的一致性,使得网络能够正常处理。 方法:将所有输入图像调整为特定的尺寸(例如224x224像素&a…...

音视频开发4-补充 FFmpeg 开发环境搭建 -- 在windows 上重新build ffmpeg
本节的目的是在windows 上 编译 ffmpeg 源码,这样做的目的是:在工作中可以根据工作的实际内容裁剪 ffmpeg,或者改动 ffmpeg 的源码。 第一步 :下载, 安装,配置 ,运行 msys64 下载 下载地址&…...
第十二周笔记
微信小程序的自定义事件是指开发者可以自行定义并触发的事件,以实现特定的功能或逻辑。通过自定义事件,开发者可以更灵活地管理小程序的交互和数据流动,提升用户体验和开发效率。下面我将详细讲解微信小程序自定义事件,包括定义、…...

SketchUp v2024 v24.0.553 解锁版安装教程 (强大的绘图三维建模工具)
前言 SketchUp(简称SU,俗称草图大师)全球知名的三维建模软件,强大的绘图工具、建模渲染、扩展插件和渲染器模板、海量3D模型库及建模灯光材质渲染效果图,用于建筑师、城市规划专家、游戏开发等行业。 一、下载地址 …...
力扣题解记录
三元组队列、取出元组中的元素:腐烂的橘子...
Flutter 中的 ExpandIcon 小部件:全面指南
Flutter 中的 ExpandIcon 小部件:全面指南 Flutter 提供了一系列的动画图标,ExpandIcon 就是其中之一,它用于表示一个可以展开或收起的内容区域。这个小部件通常用于实现折叠列表、手风琴菜单或其他类似的UI元素。本文将为您提供一个全面的指…...

想转行程序员的朋友,有什么想问的在评论区随便问,我知道的都告诉你。
你想转行程序员吗? 我自己是法学院毕业后,通过2年的努力才转行程序员成功的。 我发现对于一个外行来说,找不到一个适合自己的方向,光靠努力在一个新的行业里成功异常艰难。即使你非常努力,但方向错了也会做大量的无用…...

Jenkins工具系列 —— 通过钉钉API 发送消息
文章目录 钉钉环境搭建使用钉钉API接口 发送消息机器人安全设置使用自定义关键词机器人安全设置使用加签方式 资料下载 钉钉环境搭建 在jenkins安装钉钉插件以及小机器人,这部分内容可参考:插件 钉钉发送消息 使用钉钉API接口 发送消息 机器人安全设置…...

MySQL--存储引擎
一、存储引擎介绍 1.介绍 存储引擎相当于Linux的文件系统,以插件的模式存在,是作用在表的一种属性 2.MySQL中的存储引擎类型 InnoDB、MyISAM、CSV、Memory 3.InnoDB核心特性的介绍 聚簇索引、事务、MVCC多版本并发控制、行级锁、外键、AHI、主从复制特…...

【经典文献】光-声立体成像:关于系统标定与三维目标重建
论文名称:《Opti-Acoustic Stereo Imaging: On System Calibration and 3-D Target Reconstruction》作者列表:Shahriar Negahdaripour, Hicham Sekkati, and Hamed Pirsiavash作者单位:美国迈阿密大学电气与计算机工程系,佛罗里达…...

弘君资本股市行情:股指预计保持震荡上扬格局 关注汽车、银行等板块
弘君资本指出,近期商场体现全体分化,指数层面上看,沪指一路震动上行,创出年内新高,创业板指和科创50指数体现相对较弱,依然是底部震动走势。从盘面体现上看,轮动依然是当时商场的主基调…...

看这两位东北圣女美吗?如何描写美女的大长腿?
看这两位东北圣女美吗?如何描写美女的大长腿? 最近署名为懂球娘娘的一篇描写东北圣女的文章火了,文中描述了海棠朵朵与辛芷蕾这两位娇媚动人的角色。其美艳动人的形象和魅力四溢的描写让人为之倾倒。 这种通过文字展现人物魅力的能力让人佩服…...

Linux相关指令
目录 1、输出重定向 2、追加重定向 3、输出重定向 4、more 5、less 6、head 7、tail 8、| (管道) 9、wc 10、与时间相关的指令 11、cal 12、find 13、grep 14、zip/unzip 1、输出重定向 在linux中,可以用echo向屏幕中输出字符串: 这是向屏幕…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...