自己手写tomcat项目

一:Servlet的原理

在Servlet(接口中)有:

1.init():初始化servlet

2.getServletConfig():获取当前servlet的配置信息

3.service():服务器(在HttpServlet中实现,目的是为了更好的匹配http的请求方式)

4.getServletInfo():获取servlet当前运行过程中的信息

5.destroy():销毁,回收内存

在I/O中应包含:1.请求头(f12+网络) 2.请求方式(get/post) 3.请求内容

get请求:将请求的内容放在url中(相对不安全),url长度有限(导致发送的内容不能太长),get请求做查询

post请求:请求内容放在请求体当中,无法看到(相对安全),一般用来做文件上传、下载,post请求做增删改操作

在ServletRequest中应该有:1.method:请求方式 2.编码方式 3.parmater 4.url 5.cookie

在ServletResponse中应该有:1.状态码 2.编码方式 3.字符集 4.data数据

每一个和外界进行通讯的进程(端口号0~65535)

端口号区分当前的进程

serverSocket.accept():阻塞监听(停在这里等到程序的到来)

二:tomcat原理

下面用一个图给大家展示

注解:给程序看的(@webservlet等等)

注释:给人看得(//)

实现自定义注解:需要使用jdk提供元注解(主要使用前两个)

1.@Target注解(用来描述注解的使用范围)

2.@Retention注解(表示这个注解在什么时候还有效,用于描述注解的生命周期)

3.@Documented注解

4.@Inherited注解 这四个帮助实现自定义注解

前两个的使用方法

1.

2.

利用上面两个就能够实现自定义注解(下面就是实现)

三、手写tomcat

首先先创建idea项目(名称和位置可以随意)

在创建好项目之后会有一个.java文件(可删可不删)

之后在创建下面几个软件包和MyTomcat文件(MyTomcat和刚开始有的那个java文件不一样)

之后在每个软件包中创建好java文件和注解文件、接口文件(其中zj软件包中的注解文件可用自己的姓名起,因为是自己手写的tomcat文件)

创建完成之后,首先在HttpServletRequest和HttpServletResponse中分别写

package com.qcby.request;public class HttpServletRequest {private String method;private String url;public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}
}
package com.qcby.response;import com.qcby.request.HttpServletRequest;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());}
}

写好之后就可以在写servlet软件包中的内容了

这是接口

package com.qcby.servlet;import com.qcby.request.HttpServletRequest;
import com.qcby.response.HttpServletResponse;import java.io.IOException;public interface servlet {public void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
}

这是java

package com.qcby.servlet;import com.qcby.request.HttpServletRequest;
import com.qcby.response.HttpServletResponse;import java.io.IOException;public abstract class HttpServlet implements servlet{public void doPost(HttpServletRequest request, HttpServletResponse response) {}public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {}@Overridepublic void service(HttpServletRequest request, HttpServletResponse response) throws IOException {if(request.getMethod().equals("GET")){doGet(request,response);}else if(request.getMethod().equals("POST")){doPost(request,response);}}
}

写关于自己的注解

package com.qcby.zj;import java.lang.annotation.*;@Target(ElementType.TYPE)//该注解用在类上面
@Retention(RetentionPolicy.RUNTIME)//在运行期间表达
public @interface LRHServlet {String url();
}

之后写myweb中的

package com.qcby.myweb;import com.qcby.request.HttpServletRequest;
import com.qcby.response.HttpServletResponse;
import com.qcby.servlet.HttpServlet;
import com.qcby.util.ResponseUtil;
import com.qcby.zj.LRHServlet;import java.io.IOException;@LRHServlet(url="/myservlet")
public class MyFirstServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("hello word");response.writeServlet(ResponseUtil.getResponseHeader200("First hello word"));}@Overridepublic void doPost(HttpServletRequest request,HttpServletResponse response){}
}
package com.qcby.myweb;import com.qcby.request.HttpServletRequest;
import com.qcby.response.HttpServletResponse;
import com.qcby.servlet.HttpServlet;
import com.qcby.zj.LRHServlet;@LRHServlet(url="/insert")
public class insertServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response){System.out.println("I am insert");}@Overridepublic void doPost(HttpServletRequest request,HttpServletResponse response){}
}

util中放的是工具类,可直接复制

package com.qcby.util;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;/*** 扫描指定包,获取该包下所有的类的全路径信息*/
public class SearchClassUtil {/*** 扫描指定包路径下的所有类* @param basePack 需要扫描的包名* @return 类的全路径列表*/public static List<String> searchClass(String basePack) {//创建存储类路径的列表List<String> classPaths = new ArrayList<>();try {// 获取类加载器(用于查找类路径资源)ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 将包名转换为路径格式(如 com.qcby.test)转换为路径格式(com/qcby/test)String path = basePack.replace('.', '/');// 获取资源枚举通过类加载器获取该路径下的所有资源(可能来自文件系统或 JAR 包)Enumeration<URL> resources = classLoader.getResources(path);//resources 是通过类加载器获取的资源枚举(包含文件系统和 JAR 包中的资源)while (resources.hasMoreElements()) {//每次循环处理一个资源 URLURL resource = resources.nextElement();// 判断资源类型(文件系统或JAR包)if (resource.getProtocol().equalsIgnoreCase("file")) {// 处理文件系统中的类//获取 URL 中的路径部分String filePath = resource.getPath();//解码特殊字符filePath = java.net.URLDecoder.decode(filePath, "UTF-8");//调用 findAndAddClassesInPackageByFile() 递归扫描目录findAndAddClassesInPackageByFile(basePack, filePath, classPaths);} else if (resource.getProtocol().equalsIgnoreCase("jar")) {// 处理JAR包中的类//从 JAR 资源 URL 中提取实际的 JAR 文件路径。跳过前缀 jar:file:(长度为 5)。//indexOf("!"):找到 ! 的位置,截取到此前的部分,得到 JAR 文件的路径String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));//解码 URL 编码的特殊字符jarPath = java.net.URLDecoder.decode(jarPath, "UTF-8");//打开 JAR 文件,扫描指定包下的所有类文件。findAndAddClassesInPackageByJar(basePack, jarPath, classPaths);}}} catch (Exception e) {e.printStackTrace();}return classPaths;}/*** 从文件系统中查找类*/private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, List<String> classPaths) {//创建文件对象将传入的路径字符串转换为 File 对象,用于文件系统操作。File dir = new File(packagePath);//确保当前路径是一个存在且有效的目录。if (!dir.exists() || !dir.isDirectory()) {return;}// 获取目录下的所有文件和子目录,并过滤出以下两类File[] dirfiles = dir.listFiles(file ->// 文件夹或.class文件//使用 Lambda 表达式作为 listFiles 的过滤器,简洁地指定筛选条件。file.isDirectory() || file.getName().endsWith(".class"));//目录无法访问(如权限不足)或为空时,listFiles 可能返回 null,此处直接跳过。if (dirfiles == null) {return;}// 遍历所有文件for (File file : dirfiles) {if (file.isDirectory()) {// 递归处理子目录findAndAddClassesInPackageByFile(packageName + "." + file.getName(), //包名file.getAbsolutePath(),   //文件或目录的绝对路径classPaths    //类路径集合(用于储存结果));} else {// 处理class文件String className = file.getName().substring(0, file.getName().length() - 6);classPaths.add(packageName + '.' + className);}}}/*** 从JAR包中查找类*/private static void findAndAddClassesInPackageByJar(String packageName, String jarPath, List<String> classPaths) {//尝试打开 JAR 文件(资源自动关闭)try (JarFile jar = new JarFile(jarPath)) {//获取 JAR 文件中所有条目的枚举Enumeration<JarEntry> entries = jar.entries();//逐个处理 JAR 中的每个条目。while (entries.hasMoreElements()) {JarEntry entry = entries.nextElement();String name = entry.getName();// 判断是否是类文件并且在指定包路径下//确保只提取指定包及其子包下的类文件。if (name.endsWith(".class") && name.startsWith(packageName.replace('.', '/'))) {//提取类全限定名去除 .class 后缀:String className = name.substring(0, name.length() - 6).replace('/', '.');//添加类名到结果列表classPaths.add(className);}}} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {// 测试示例//测试扫描指定包List<String> classes = searchClass("com.qcby.myweb");//遍历并打印结果for (String className : classes) {System.out.println(className);}}public static List<String> searchClass() {return null;}
}
package com.qcby.util;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;}
}

在config中写入

package com.qcby.config;import com.qcby.servlet.HttpServlet;
import com.qcby.util.SearchClassUtil;
import com.qcby.zj.LRHServlet;import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;/*** tomcat路由*/
public class TomcatRoute {//HashMap<String, HttpServlet> HttpServlet向上转型(使用多态),因为myweb中的父类全是HttpServlet//将public static HashMap<String, HttpServlet> routes = new HashMap<>();写入static{}中会导致无法调用public static HashMap<String, HttpServlet> routes = new HashMap<>();static{// 传入要扫描的包名List<String> paths = SearchClassUtil.searchClass("com.qcby.myweb");for(String path : paths){try{//urlClass clazz = Class.forName(path);LRHServlet webServlet = (LRHServlet) clazz.getDeclaredAnnotation(LRHServlet.class);routes.put(webServlet.url(), (HttpServlet) clazz.getDeclaredConstructor().newInstance());System.out.println(webServlet.url());
//                if (webServlet != null) {
//                    System.out.println(webServlet.url());
//                }//对象clazz.getDeclaredConstructor().newInstance();}catch(ClassNotFoundException e){e.printStackTrace();} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}}
}

最后就是直接的MyTomcat

package com.qcby;import com.qcby.config.TomcatRoute;
import com.qcby.request.HttpServletRequest;
import com.qcby.response.HttpServletResponse;
import com.qcby.servlet.HttpServlet;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;/*** tomcat主启动类*/
public class MyTomcat {static HashMap<String, HttpServlet> routes = TomcatRoute.routes;static HttpServletRequest request = new HttpServletRequest();/*** 分发器*/public static void dispatch(HttpServletResponse response) throws IOException {HttpServlet servlet = routes.get(request.getUrl());System.out.println(servlet);if (servlet != null) {servlet.service(request, response);}}public static void start() throws IOException {System.out.append("服务器端启动...");//1.定义ServerSocket对象进行服务器端的端口注册ServerSocket serverSocket = new ServerSocket(8080);while (true) {//2.监听客户端的Socket链接程序Socket socket = serverSocket.accept(); // 阻塞监听//3.打开输入流,解析客户端发来的内容InputStream inputStream = socket.getInputStream();//输入流BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));//将字节流转换成字符流String str = reader.readLine();request.setMethod(str.split("\\s")[0]);request.setUrl(str.split("\\s")[1]);//4.打开输出流OutputStream outputStream = socket.getOutputStream();HttpServletResponse response = new HttpServletResponse(outputStream);dispatch(response);}}public static void main(String[] args) throws IOException {start();}
}

这样就完成三分之二了

之后运行MyTomcat程序后,回显示com.qcby.myweb下的所有软件包名,和服务器端启动的字样

在浏览器的地址栏里输入(localhost:8080/myservlet)这里的8080要根据你实际写的端口号是多少写多少(myservlet要写自己定义的url)

在浏览器中就能够看到下面写在MyFirstServlet中的内容

在控制台能够看到MyFirstservlet中打印的内容

这样一个简单的tomcat就写好了

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

兰亭妙微:用系统化思维重构智能座舱 UI 体验

兰亭妙微设计专注于以产品逻辑驱动的界面体验优化&#xff0c;服务领域覆盖AI交互、智能穿戴、IoT设备、智慧出行等多个技术密集型产业。我们倡导以“系统性设计”为方法论&#xff0c;在用户需求与技术边界之间找到最优解。 此次智能驾驶项目&#xff0c;我们为某车载平台提供…

ubuntu安装google chrome

更新系统 sudo apt update安装依赖 sudo apt install curl software-properties-common apt-transport-https ca-certificates -y导入 GPG key curl -fSsL https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor | sudo tee /usr/share/keyrings/google-chrom…

技术测评:小型单文件加密工具的功能解析

最近在测试一款名为OEMexe的文件加密工具&#xff0c;发现它确实有一些独特之处值得分享。这款软件体积非常小巧&#xff0c;仅209KB&#xff0c;属于绿色单文件版程序&#xff0c;无需安装即可直接运行。 主要特点 多格式支持&#xff1a;能够处理多种常见文件格式&#xff0…

Java-Objects类高效应用的全面指南

Java_Objects类高效应用的全面指南 前言一、Objects 类概述二、Objects 类的核心方法解析2.1 requireNonNull系列方法&#xff1a;空指针检查的利器2.2 equals方法&#xff1a;安全的对象比较2.3 hashCode方法&#xff1a;统一的哈希值生成2.4 toString方法&#xff1a;灵活的对…

计网| 网际控制报文协议(ICMP)

目录 网际控制报文协议&#xff08;ICMP&#xff09; 一、ICMP 基础特性 二、ICMP 报文分类及作用 差错报告报文 询问报文 网际控制报文协议&#xff08;ICMP&#xff09; ICMP&#xff08;Internet Control Message Protocol&#xff0c;网际控制报文协议&#xff09;是 …

微服务初步学习

系统架构演变过程 一、单体架构 前后端都在一个项目中&#xff0c;包括我们现在的前后端分离开发&#xff0c;都可以看作是一个单体项目。 二、集群架构 把一个服务部署多次&#xff0c;可以解决服务不够的问题&#xff0c;但是有些不必要的功能也跟着部署多次。 三、垂直架…

Web安全基础:深度解析与实战指南

一、Web安全体系架构的全面剖析 1.1 分层防御模型(Defense in Depth) 1.1.1 网络层防护 ​​防火墙技术​​: 状态检测防火墙(SPI):基于连接状态跟踪,阻断非法会话(如SYN Flood攻击)下一代防火墙(NGFW):集成IPS、AV、URL过滤(如Palo Alto PA-5400系列)配置示例…

使用大语言模型从零构建知识图谱(上)

从零到一&#xff1a;大语言模型在知识图谱构建中的实操指南 ©作者|Ninja Geek 来源|神州问学 将你的 Pandas data frame 利用大语言模型转换为知识图谱。从零开始构建自己的基于大语言模型的图谱构建器&#xff0c;实际使用 Langchain 的 LLMGraphTransformer &#xff…

18.自动化生成知识图谱的多维度质量评估方法论

文章目录 一、结构维度评估1.1 拓扑结构评估1.1.1 基础图论指标1.1.2 层级结构指标 1.2 逻辑一致性评估1.2.1 形式逻辑验证1.2.2 约束满足度 二、语义维度评估2.1 语义一致性评估2.1.1 标签语义分析2.1.2 关系语义评估 2.2 语义表示质量2.2.1 嵌入质量2.2.2 上下文语义评估 三、…

go 集成base64Captcha 支持多种验证码

base64Captcha 是一个基于 Go 语言开发的验证码生成库&#xff0c;主要用于在 Web 应用中集成验证码功能&#xff0c;以增强系统的安全性。以下是其主要特点和简介&#xff1a; base64Captcha主要功能 验证码类型丰富&#xff1a;支持生成多种类型的验证码&#xff0c;包括纯…

制作大风车动画

这个案例的风车旋转应用了图形变换来实现&#xff0c;速度和缩放比例应用slider来实现&#xff0c;其中图片的速度&#xff0c;图片大小的信息通过State来定义变量管理&#xff0c;速度和和缩放比例的即时的值通过Prop来管理。 1. 案例效果截图 2. 案例运用到的知识点 2.1. 核…

代码随想录算法训练营第四十二四十三天

LeetCode/卡码网题目: 42. 接雨水84. 柱状图中最大的矩形98. 所有可达路径 其他: 今日总结 往期打卡 42. 接雨水 跳转: 42. 接雨水 学习: 代码随想录公开讲解 问题: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能…

SEO 优化实战:ZKmall模板商城的 B2C商城的 URL 重构与结构化数据

在搜索引擎算法日益复杂的今天&#xff0c;B2C商城想要在海量信息中脱颖而出&#xff0c;仅靠优质商品和营销活动远远不够。ZKmall模板商城以实战为导向&#xff0c;通过URL 重构与结构化数据优化两大核心策略&#xff0c;帮助 B2C 商城实现从底层架构到搜索展示的全面升级&…

Linux自有服务

自有服务概述 概述 自有服务&#xff0c;即不需要用户独立去安装的软件的服务&#xff0c;而是当系统安装好之后就可以直接使用的服务&#xff08;内置&#xff09; 显示服务 显示服务 命令&#xff1a;systemctl \[选项] 选项参数 list-units --type service --all&#x…

ZYNQ Overlay硬件库使用指南:用Python玩转FPGA加速

在传统的FPGA开发中,硬件设计需要掌握Verilog/VHDL等硬件描述语言,这对软件开发者而言门槛较高。Xilinx的PYNQ框架通过Overlay硬件库彻底改变了这一现状——开发者只需调用Python API即可控制FPGA的硬件模块,实现硬件加速与灵活配置。本文将深入探讨ZYNQ Overlay的核心概念、…

JavaScript入门【1】概述

1.JavaScript是什么? <font style"color:rgb(38,38,38);">Javascript &#xff08;简称“JS”&#xff09;是⼀种直译式脚本语⾔&#xff0c;⼀段脚本其实就是⼀系列指令&#xff0c;计算机通过这些指令来达成⽬标。它⼜是⼀种动态类型的编程语⾔。JS⽤来在⽹…

c++从入门到精通(五)--异常处理,命名空间,多继承与虚继承

异常处理 栈展开过程&#xff1a; 栈展开过程沿着嵌套函数的调用链不断查找&#xff0c;直到找到了与异常匹配的catch子句为止&#xff1b;也可能一直没找到匹配的catch&#xff0c;则退出主函数后查找过程终止。栈展开过程中的对象被自动销毁。 在栈展开的过程中&#xff0c…

自适应稀疏核卷积网络:一种高效灵活的图像处理方案

自适应稀疏核卷积网络&#xff1a;一种高效灵活的图像处理方案 引言 在深度学习的大潮中&#xff0c;计算机视觉技术取得了长足的进步。其中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;作为图像处理的核心工具&#xff0c;极大地推动了各类图像识别任务的效果提升。…

Nginx:利用 FreeSSL 申请(Https)免费证书的技术指南

1、简述 在现代互联网应用中,使用 HTTPS 连接是确保数据传输安全的基本需求。SSL/TLS 证书能够加密客户端与服务器之间的通信,防止中间人攻击等安全隐患。而许多开发者和小型企业可能会担心 SSL 证书的费用问题。幸运的是,FreeSSL 提供了一个简单易用的平台,允许我们申请免…

自定义库模块增加自定义许可操作详细方法

自定义库模块增加自定义许可操作详细方法 用到的工具: 后面程序用到的所有代码均是该工具生成的秘密&#xff01;&#xff01;&#xff01;&#xff01; 【切记切记&#xff01;&#xff01;&#xff01; 一定要记住密码&#xff0c;不然如果你想将库的许可认证移除&#xf…