深入核心:一步步手撕Tomcat搭建自己的Web服务器

介绍:

        servlet:处理 http 请求

        tomcat:服务器

Servlet

  1.  servlet 接口:
    1. 定义 Servlet 声明周期
    2. 初始化:init
    3. 服务:service
    4. 销毁:destory
  2. 继承链:

Tomcat

  1. Tomcat 和 servlet 原理:
    1. 浏览器向服务器发送 http 请求
    2. socket 接收请求,发送给请求解析器
    3. 请求解析器再解析自己想要的信息
      1. 请求地址
      2. 请求方式
      3. 浏览器类型
      4. Cookie
      5. ··········
    4. 解析器解析到的信息发送给映射器
    5. 映射器中存放:
      1. Web 地址
      2. 内存地址
    6. 根据请求解析器中解析的信息,找到映射器中相对应的网络地址和内存地址,根据请求方式去访问对应的程序
  2. Socket 交互以及解析阶段:
    package com.Tomcat;import com.sun.corba.se.spi.activation.Server;import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;public class myTomcat {Request request = new Request();    //创建解析请求的对象public void startUP() throws IOException {//监听端口号ServerSocket serverSocket = new ServerSocket(7421);while(true){Socket socket = serverSocket.accept();      //阻塞监听System.out.println("有请求!!!!!!!");//将每个请求开启一个线程new Thread(new Runnable() {@Overridepublic void run() {try {handler(socket);    //调用处理信息方法}catch (Exception e){e.printStackTrace();}}}).start();}}//处理信息public void handler(Socket socket) throws IOException {InputStream inputStream = socket.getInputStream();//将bit流转换为文字信息int count = 0;while(count == 0){count = inputStream.available();        //统计输入流的长度}//打印数据byte[] bytes = new byte[count];inputStream.read(bytes);    //将bit信息写入到byte数组String Context = new String(bytes);     //将 byte 数组转换为字符串System.out.println(Context);    //输出信息//拆解字符串,获取想要的信息String[] list = Context.split("\\n");   //根据换行切割字符串String Methed = list[0].split(" ")[0];  //在拆分的第一行中以空格再次拆分,获取第一个数据String Path = list[0].split(" ")[1];    //在拆分的第一行中以空格再次拆分,获取第二个数据//把截取的数据传给 Request 类request.setMethod(Methed);request.setPath(Path);}
    }
  3. Request 存储解析信息 ==> 继承 HttpservletRequest,为方便访问同一变量
    public class Request implements HttpservletRequast {private String Method;private String Path;public String getMethod() {return Method;}public void setMethod(String method) {Method = method;}public String getPath() {return Path;}public void setPath(String path) {Path = path;}@Overridepublic String toString() {return "Request{" +"Method='" + Method + '\'' +", Path='" + Path + '\'' +'}';}}
  4. 扫包:
    /*** 扫描指定包,获取该包下所有的类的全路径信息*/
    public class SearchClassUtil {//存放文件的绝对路径public static  List<String> classPaths = new ArrayList<String>();/*** 扫描固定包下面的类* @return*/public static List<String> searchClass(){//需要扫描的包名String basePack = "com.servlet";      //写需要获取包名的路径//将获取到的包名转换为路径//getResource():是获取类对象的方法, "/" :表示在根目录开始//getPath():是将对象的路径转为字符串String classPath = SearchClassUtil.class.getResource("/").getPath();//将包名转换为文件系统路径  --->  把 "." 替换成 系统的路径分隔符(系统不一样,分隔符也不一样)basePack =  basePack.replace(".", File.separator);//把两个路径合并为文件的 绝对路径String searchPath = classPath + basePack;//调用doPath()方法,把路径写入路径数组中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);}}
    }
  5. 注解:设置文件的访问地址 ==> HashMap 中的 key 值
    @Retention(RetentionPolicy.RUNTIME)     //在运行期间保留
    @Target(ElementType.TYPE)       //作用于类上面
    public @interface Webservlet {String url() default "";
    }
  6. 创建 Httpservlet 实现 Service 服务:
    public abstract class HttpServlet {   //HttpServerlet只实现了父类的service服务,其他方法没有实现,此时该类为抽象类//子类需要使用doGet或doPost方法,在这里直接让子类去实现两个发给发//这里需要获取Request中被解析出来的数据,//要想访问的是同一个Request对象,这里用到接口,让Request实现这个接口,传参时就会向上转型,此时request对象为同一个对象public abstract void doGet(HttpServletRequast requast, HttpServletResponse response) throws Exception;public abstract void doPost(HttpServletRequast requast,HttpServletResponse response);//在服务中判断用户的请求方式,让子类实现向对应的方法public void service(HttpServletRequast requast,HttpServletResponse response) throws Exception {if(requast.getMethod().equals("GET")){doGet(requast,response);}else if(requast.getMethod().equals("POST")){doPost(requast,response);}}
    }
  7. HttpservletRequast:为 Httpservlet 访问对象为统一对象,让 Request 实现这个接口
    public interface HttpservletRequast {String getMethod();void setMethod(String method);String getPath();void setPath(String path);
    }
  8. 自己创建 servlet,继承 Httpservlet 实现 service 服务  ==>  实现相关的访问方式
    @WebServerlet(url = "OneServerlet")
    public class FirstServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequast requast, HttpServletResponse response) throws Exception {}@Overridepublic void doPost(HttpServletRequast requast, HttpServletResponse response) {}
    }
  9. 获取访问地址:HashMap 中的 key 值
    public class getMessageUtil {public static String fund(String path) throws Exception {//创建类对象Class clazz = Class.forName(path);//根据类对象调用 getDeclaredAnnotation() 方法找到该类的访问地址(@Webservlet()中的内容)Webservlet webservlet = (Webservlet) clazz.getDeclaredAnnotation(Webservlet.class);return webservlet.url();}public static void main(String[] args) throws Exception {//fund();}
    }
  10. 映射器:底层由 HashMap 容器存储
    public class ServletConfigMapping {//将执行逻辑写入static代码块中,以便更好加载//定义Servlet容器public static Map<String,Class<HttpServlet>> classMap = new HashMap<>();//该静态代码块应放在启动tomcat前运行static {List<String> classPaths = SearchClassUtil.searchClass();for (String classPath : classPaths){try {InitClassMap(classPath);}catch (Exception e){e.printStackTrace();}}}//将key val 值插入到HashMap中public static void InitClassMap(String classPath) throws Exception {//获取类对象Class clazz = Class.forName(classPath);//获取访问地址String url = getMessageUtil.fundUrl(classPath);//将值插入HashMap树当中classMap.put(url,clazz);}
    }

Response 返回数据:

  1. 设置返回头工具类:
    /*** 设置返回头*/
    public class ResponseUtil {public  static  final String responseHeader200 = "HTTP/1.1 200 \r\n"+"Content-Type:text/html \r\n"+"\r\n";public  static  final String responseHeader200JSON = "HTTP/1.1 200 \r\n"+"Content-Type:application/json \r\n"+"\r\n";public static String getResponseHeader404(){return "HTTP/1.1 404 \r\n"+"Content-Type:text/html \r\n"+"\r\n" + "404";}public static String getResponseHeader200(String context){return "HTTP/1.1 200 \r\n"+"Content-Type:text/html \r\n"+"\r\n" + context;}
    }
  2. 读取文件:根据提供的地址转化为文件完整地址
    /*** 该类的主要作用是进行读取文件*/
    public class FileUtil {public  static  boolean witeFile(InputStream inputStream, OutputStream outputStream){boolean success = false ;BufferedInputStream bufferedInputStream ;BufferedOutputStream bufferedOutputStream;try {bufferedInputStream = new BufferedInputStream(inputStream);bufferedOutputStream = new BufferedOutputStream(outputStream);bufferedOutputStream.write(ResponseUtil.responseHeader200.getBytes());int count = 0;while (count == 0){count = inputStream.available();}int fileSize = inputStream.available();long written = 0;int beteSize = 1024;byte[] bytes = new byte[beteSize];while (written < fileSize){if(written + beteSize > fileSize){beteSize = (int)(fileSize - written);bytes = new byte[beteSize];}bufferedInputStream.read(bytes);bufferedOutputStream.write(bytes);bufferedOutputStream.flush();written += beteSize;}success = true;} catch (IOException e) {e.printStackTrace();}return success;}public static boolean writeFile(File file,OutputStream outputStream) throws Exception{return witeFile(new FileInputStream(file),outputStream);}/*** 根据提供的地址转换为文件完整地址* @param path* @return*/public static String getResoucePath(String path){String resource = FileUtil.class.getResource("/").getPath();return resource + "\\" + path;}}
  3. response 返回数据:
    public class Response implements HttpServletResponse {//输出流private OutputStream outputStream;public Response(OutputStream outputStream){this.outputStream = outputStream;}/**** 返回动态文字信息* @param context* @throws IOException*/public void write(String context) throws IOException {outputStream.write(context.getBytes());     //将文字信息转换为 byte流 形式}public void WriteHtml(String Path) throws Exception {//得到文件全路径String resoucePath = FileUtil.getResoucePath(Path);//创建文件File file = new File(resoucePath);if(file.exists()){System.out.println("静态资源存在");//输出静态资源FileUtil.writeFile(file,outputStream);}else {System.out.println("静态资源不存在");}}
    }
  4. HttpServletResponse 接口:
    public interface HttpServletResponse {void write(String context) throws IOException;
    }
  5. 输出资源:
    Response response = new Response(socket.getOutputStream());if(request.getPath().equals("") || request.getPath().equals("/")){      //空访问response.WriteHtml("404.html");     //抛出404页面response.write(ResponseUtil.getResponseHeader404());    //抛出404文字信息} else if (ServerletConfigMapping.classMap.get(request.getPath()) == null) {        //静态资源response.WriteHtml(request.getPath());}else {     //动态资源Class<HttpServlet> httpServletClass = ServerletConfigMapping.classMap.get(request.getPath());   //获取类对象if(httpServletClass != null){       //有类对象HttpServlet httpServlet = httpServletClass.newInstance();   //多态创建对象httpServlet.service(request,response);      //启动service服务}else{      //没有动态资源response.WriteHtml("404.html");     //抛出 404页面}
    }
  6. 整合后 Tomcat:
    public class myTomcat {Request request = new Request();    //创建解析请求的对象//提前加载容器(HashMap)static {List<String> classPaths = SearchClassUtil.searchClass();for (String classPath : classPaths){try {ServerletConfigMapping.InitClassMap(classPath);}catch (Exception e){e.printStackTrace();}}}public static void main(String[] args) throws IOException {myTomcat myTomcat = new myTomcat();myTomcat.startUP();}public void startUP() throws IOException {//监听端口号ServerSocket serverSocket = new ServerSocket(8080 );while(true){Socket socket = serverSocket.accept();      //阻塞监听System.out.println("有请求!!!!!!!");//将每个请求开启一个线程new Thread(new Runnable() {@Overridepublic void run() {try {handler(socket);    //调用处理信息方法}catch (Exception e){e.printStackTrace();}}}).start();}}//处理信息public void handler(Socket socket) throws Exception {InputStream inputStream = socket.getInputStream();//将bit流转换为文字信息int count = 0;while(count == 0){count = inputStream.available();        //统计输入流的长度}//打印数据byte[] bytes = new byte[count];inputStream.read(bytes);    //将bit信息写入到byte数组String Context = new String(bytes);     //将 byte 数组转换为字符串System.out.println(Context);    //输出信息//拆解字符串,获取想要的信息String[] list = Context.split("\\n");   //根据换行切割字符串String Methed = list[0].split(" ")[0];  //在拆分的第一行中以空格再次拆分,获取第一个数据String Path = list[0].split(" ")[1];    //在拆分的第一行中以空格再次拆分,获取第二个数据//把截取的数据传给 Request 类request.setMethod(Methed);request.setPath(Path);//判断资源类型Response response = new Response(socket.getOutputStream());if(request.getPath().equals("") || request.getPath().equals("/")){      //空访问response.WriteHtml("404.html");     //抛出404页面response.write(ResponseUtil.getResponseHeader404());    //抛出404文字信息} else if (ServerletConfigMapping.classMap.get(request.getPath()) == null) {        //静态资源response.WriteHtml(request.getPath());}else {     //动态资源Class<HttpServlet> httpServletClass = ServerletConfigMapping.classMap.get(request.getPath());   //获取类对象if(httpServletClass != null){       //有类对象HttpServlet httpServlet = httpServletClass.newInstance();   //多态创建对象httpServlet.service(request,response);      //启动service服务}else{      //没有动态资源response.WriteHtml("404.html");     //抛出 404页面}}}
    }

Tomcat 运行原理:

  1. 原理:
    1. 浏览器发起请求
    2. Socket 解析输入流,获取请求头信息
    3. 分析请求的地址是动态资源还是静态资源
      1. 首先判断 HashMap 中有没有这个 Key 值
      2. 如果有就去访问动态资源,如果没有就去查看静态资源
      3. 如果也不是静态资源就返回 404
    4. Servlet 容器(HashMap):
      1. 将 @WebServlet 中的值作为 key 值,将对象作为 value 值,存入 HashMap 中
  2. Servlet 容器加载时期:
    1. 在 Socket 启动之前启动 Servlet 容器
      1. 缺点:程序启动时间变长
      2. 优点:不易出现空指针
    2. 在 Socket 启动之后启动 Servlet 容器
    3. 在浏览器访问的同时启动 Servlet 容器

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

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

相关文章

傅里叶分析之掐死教程

https://zhuanlan.zhihu.com/p/19763358 要让读者在不看任何数学公式的情况下理解傅里叶分析。 傅里叶分析 不仅仅是一个数学工具&#xff0c;更是一种可以彻底颠覆一个人以前世界观的思维模式。但不幸的是&#xff0c;傅里叶分析的公式看起来太复杂了&#xff0c;所以很多…

【Linux系统】信号:认识信号 与 信号的产生

信号快速认识 1、生活角度的信号 异步&#xff1a;你是老师正在上课&#xff0c;突然有个电话过来资料到了&#xff0c;你安排小明过去取资料&#xff0c;然后继续上课&#xff0c;则小明取资料这个过程就是异步的 同步&#xff1a;小明取快递&#xff0c;你停下等待小明回来再…

stm32硬件实现与w25qxx通信

使用的型号为stm32f103c8t6与w25q64。 STM32CubeMX配置与引脚衔接 根据stm32f103c8t6引脚手册&#xff0c;采用B12-B15四个引脚与W25Q64连接&#xff0c;实现SPI通信。 W25Q64SCK&#xff08;CLK&#xff09;PB13MOSI&#xff08;DI&#xff09;PB15MISO(DO)PB14CS&#xff08…

22.Word:小张-经费联审核结算单❗【16】

目录 NO1.2 NO3.4​ NO5.6.7 NO8邮件合并 MS搜狗输入法 NO1.2 用ms打开文件&#xff0c;而不是wps❗不然后面都没分布局→页面设置→页面大小→页面方向→上下左右&#xff1a;页边距→页码范围&#xff1a;多页&#xff1a;拼页光标处于→布局→分隔符&#xff1a;分节符…

it基础使用--5---git远程仓库

it基础使用–5—git远程仓库 1. 按顺序看 -git基础使用–1–版本控制的基本概念 -git基础使用–2–gti的基本概念 -git基础使用–3—安装和基本使用 -git基础使用–4—git分支和使用 2. 什么是远程仓库 在第一篇文章中&#xff0c;我们已经讲过了远程仓库&#xff0c;每个本…

aitraderv4.2开发计划,整合QMT。年化39.9%的因子与年化19.3%的策略哪个优?

原创内容第784篇&#xff0c;专注量化投资、个人成长与财富自由。 昨天我们发布的aitrader v4.1的代码&#xff1a;aitrader_v4.1系统更新|含年化39.1%的组合策略代码|backtraderopenctp实盘&#xff08;代码数据&#xff09; 星球下周代码计划&#xff1a; 1、考虑整合back…

玩转大语言模型——使用langchain和Ollama本地部署大语言模型

系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…

word2vec 实战应用介绍

Word2Vec 是一种由 Google 在 2013 年推出的重要词嵌入模型,通过将单词映射为低维向量,实现了对自然语言处理任务的高效支持。其核心思想是利用深度学习技术,通过训练大量文本数据,将单词表示为稠密的向量形式,从而捕捉单词之间的语义和语法关系。以下是关于 Word2Vec 实战…

yes镜像站群/PHP驱动的镜像站群架构实践

▍当前站群运维的三大技术困局 在近期与多个IDC服务商的交流中发现&#xff0c;传统站群系统普遍面临&#xff1a; 同步效率瓶颈&#xff1a;跨服务器内容同步耗时超过行业标准的42%SEO权重稀释&#xff1a;镜像站点重复率导致70%的站点无法进入百度前3页运维成本失控&#x…

走向基于大语言模型的新一代推荐系统:综述与展望

HightLight 论文题目&#xff1a;Towards Next-Generation LLM-based Recommender Systems: A Survey and Beyond作者机构&#xff1a;吉林大学、香港理工大学、悉尼科技大学、Meta AI论文地址&#xff1a; https://arxiv.org/abs/2410.1974 基于大语言模型的下一代推荐系统&…

Verilog语言学习总结

Verilog语言学习&#xff01; 目录 文章目录 前言 一、Verilog语言是什么&#xff1f; 1.1 Verilog简介 1.2 Verilog 和 C 的区别 1.3 Verilog 学习 二、Verilog基础知识 2.1 Verilog 的逻辑值 2.2 数字进制 2.3 Verilog标识符 2.4 Verilog 的数据类型 2.4.1 寄存器类型 2.4.2 …

智慧园区综合管理系统如何实现多个维度的高效管理与安全风险控制

内容概要 在当前快速发展的城市环境中&#xff0c;智慧园区综合管理系统正在成为各类园区管理的重要工具&#xff0c;无论是工业园、产业园、物流园&#xff0c;还是写字楼与公寓&#xff0c;都在积极寻求如何提升管理效率和保障安全。通过快鲸智慧园区管理系统&#xff0c;用…

JavaFX - 事件处理

在 JavaFX 中&#xff0c;我们可以开发 GUI 应用程序、Web 应用程序和图形应用程序。在此类应用程序中&#xff0c;每当用户与应用程序 &#xff08;节点&#xff09; 交互时&#xff0c;都会称其发生了事件。 例如&#xff0c;单击按钮、移动鼠标、通过键盘输入字符、从列表中…

小米CR6606,CR6608,CR6609 启用SSH和刷入OpenWRT 23.05.5

闲鱼上收了一台CR6606和一台CR6609, 一直没时间研究, 趁春节假期把这两个都刷成 OpenWRT 配置说明 CPU: MT7621AT&#xff0c;双核880MHz内存: NT5CC128M16JR-EKI 或 M15T2G16128A, 256MB闪存: F59L1G81MB, 128MB无线基带芯片(BB): T7905DAN无线射频芯片(RF): MT7975DN无外置F…

使用windows笔记本让服务器上网

使用windows笔记本让服务器上网 前言准备工具开始动手实践1.将手机热点打开&#xff0c;让Windows笔记本使用无线网卡连接上网2.使用网线将Windows笔记本的有线网卡和服务器的有线网卡直连3.在Windows笔记本上按winR输入ncpa.cpl打开网卡设置界面4.在Windows笔记本上右键“无线…

2007-2019年各省科学技术支出数据

2007-2019年各省科学技术支出数据 1、时间&#xff1a;2007-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、科学技术支出 4、范围&#xff1a;31省 5、指标解释&#xff1a;科学技术支出是指为促进科学研究、技术开发…

6. 使用springboot做一个音乐播放器软件项目【1.0版项目完结】附带源码~

#万物OOP 注意&#xff1a; 本项目只实现播放音乐和后台管理系统。 不分享任何音乐歌曲资源。 上一篇文章我们 做了音乐播放器后台的功能。参考地址&#xff1a; https://jsonll.blog.csdn.net/article/details/145214363 这个项目已经好几天也没更新了&#xff0c;因为临近放…

【Rust自学】15.4. Drop trait:告别手动清理,释放即安全

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.4.1. Drop trait的意义 类型如果实现了Drop trait&#xff0c;就可以让程序员自定义当值离开作用域时发生的操作。例如文件、网络资源…

2025年1月22日(网络编程 udp)

系统信息&#xff1a; ubuntu 16.04LTS Raspberry Pi Zero 2W 系统版本&#xff1a; 2024-10-22-raspios-bullseye-armhf Python 版本&#xff1a;Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习…

嵌入式C语言:大小端详解

目录 一、大小端的概念 1.1. 大端序&#xff08;Big-endian&#xff09; 1.2. 小端序&#xff08;Little-endian&#xff09; 二、大小端与硬件体系的关系 2.1. 大小端与处理器架构 2.2. 大小端与网络协议 2.3. 大小端对硬件设计的影响 三、判断系统的大小端方式 3.1.…