深入核心:一步步手撕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.rhkb.cn/news/11671.html

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

相关文章

final-关键字

一、final修饰的类不能被继承 当final修饰一个类时&#xff0c;表明这个类不能被其他类继承。例如&#xff0c;在 Java 中&#xff0c;String类就是被final修饰的&#xff0c;这保证了String类的不可变性和安全性&#xff0c;防止其他类通过继承来改变String类的行为。 final…

51单片机 01 LED

一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC&#xff1b;如果没有找到hex文件&#xff08;在objects文件夹下&#xff09;&#xff0c;在keil中options for target-output- 勾选 create hex file。 如果要修改编程 &#xff1a;重新编译-下载/编程-单片机重…

知识库建设与知识管理实践对企业发展的助推作用探索

内容概要 在当今瞬息万变的商业环境中&#xff0c;知识库建设与知识管理实践日益成为企业发展的重要驱动力。知识库作为组织内信息和知识的集成&#xff0c;起着信息存储、整理和共享的关键作用。通过有效的知识库建设&#xff0c;企业不仅能够提升员工获取信息的便利性&#…

【Pytorch和Keras】使用transformer库进行图像分类

目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理&#xff0c;transformer库能关联到huggface中对应的模型&am…

如何使用 DeepSeek 和 Dexscreener 构建免费的 AI 加密交易机器人?

我使用DeepSeek AI和Dexscreener API构建的一个简单的 AI 加密交易机器人实现了这一目标。在本文中&#xff0c;我将逐步指导您如何构建像我一样的机器人。 DeepSeek 最近发布了R1&#xff0c;这是一种先进的 AI 模型。您可以将其视为 ChatGPT 的免费开源版本&#xff0c;但增加…

ArkTS渲染控制

文章目录 if/else:条件渲染ArkUI通过自定义组件的build()函数和@Builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快…

potplayer字幕

看视频学习&#xff0c;实时字幕可以快速过滤水字数阶段&#xff0c;提高效率&#xff0c;但是容易错过一些信息。下面就是解决这一问题。 工具ptoplayer 一.生成字幕 打开学习视频&#xff0c;右键点击视频画面&#xff0c;点选字幕。勾选显示字幕。点选创建有声字幕&#…

deepseek的两种本地使用方式

总结来说 ollama是命令行 GPT4ALL桌面程序。 然后ollamaAnythingLLM可以达到桌面或web的两种接入方式。 一. ollama和deepseek-r1-1.5b和AnythingLLM 本文介绍一个桌面版的deepseek的本地部署过程&#xff0c;其中ollama可以部署在远程。 1. https://www.cnblogs.com/janeysj/p…

海外问卷调查渠道查,如何影响企业的运营

我们注意到&#xff0c;随着信息资源和传播的变化&#xff0c;海外问卷调查渠道查已发生了深刻的变化。几年前&#xff0c;市场调研是业内专家们的事&#xff0c;即使是第二手资料也需要专业人士来完成&#xff1b;但如今的因特网和许许多多的信息数据库&#xff0c;使每个人都…

TensorFlow简单的线性回归任务

如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题&#xff0c;其中输入特征 x 和标…

当卷积神经网络遇上AI编译器:TVM自动调优深度解析

从铜线到指令&#xff1a;硬件如何"消化"卷积 在深度学习的世界里&#xff0c;卷积层就像人体中的毛细血管——数量庞大且至关重要。但鲜有人知&#xff0c;一个简单的3x3卷积在CPU上的执行路径&#xff0c;堪比北京地铁线路图般复杂。 卷积的数学本质 对于输入张…

MySQL(高级特性篇) 13 章——事务基础知识

一、数据库事务概述 事务是数据库区别于文件系统的重要特性之一 &#xff08;1&#xff09;存储引擎支持情况 SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些&#xff0c;以及这些存储引擎是否支持事务能看出在MySQL中&#xff0c;只有InnoDB是支持事务的 &#x…

影视文件大数据高速分发方案

在当今的数字时代&#xff0c;影视行业的内容创作和传播方式经历了翻天覆地的变化。随着4K、8K高清视频的普及&#xff0c;以及虚拟现实(VR)和增强现实(AR)技术的发展&#xff0c;影视文件的数据量正以前所未有的速度增长。这就要求行业内的参与者必须拥有高效的大数据传输解决…

C语言教程——文件处理(2)

目录 前言 一、顺序读写函数&#xff08;续&#xff09; 1.1fprintf 1.2fscanf 1.3fwrite 1.4fread 二、流和标准流 2.1流 2.2标准流 2.3示例 三、sscanf和sprintf 3.1sprintf 3.2sscanf 四、文件的随机读写 4.1fseek 4.2ftell 4.3rewind 五、文件读取结束的…

建表注意事项(2):表约束,主键自增,序列[oracle]

没有明确写明数据库时,默认基于oracle 约束的分类 用于确保数据的完整性和一致性。约束可以分为 表级约束 和 列级约束&#xff0c;区别在于定义的位置和作用范围 复合主键约束: 主键约束中有2个或以上的字段 复合主键的列顺序会影响索引的使用&#xff0c;需谨慎设计 添加…

线性回归的损失和优化02

线性回归的损失和优化 学习目标 知道线性回归中损失函数知道使用正规方程对损失函数优化的过程知道使用梯度下降法对损失函数优化的过程 假设刚才的房子例子&#xff0c;真实的数据之间存在这样的关系&#xff1a; 真实关系&#xff1a; 真实房子价格 0.02中心区域的距离 0.…

年化18%-39.3%的策略集 | backtrader通过xtquant连接qmt实战

原创内容第785篇&#xff0c;专注量化投资、个人成长与财富自由。 大年初五&#xff0c;年很快就过完了。 其实就是本身也只是休假一周&#xff0c;但是我们赋予了它太多意义。 周五咱们发布发aitrader v4.1&#xff0c;带了backtraderctp期货的实盘接口&#xff1a; aitra…

【数据结构】_链表经典算法OJ(力扣/牛客第二弹)

目录 1. 题目1&#xff1a;返回倒数第k个节点 1.1 题目链接及描述 1.2 解题思路 1.3 程序 2. 题目2&#xff1a;链表的回文结构 2.1 题目链接及描述 2.2 解题思路 2.3 程序 1. 题目1&#xff1a;返回倒数第k个节点 1.1 题目链接及描述 题目链接&#xff1a; 面试题 …

成绩案例demo

本案例较为简单&#xff0c;用到的知识有 v-model、v-if、v-else、指令修饰符.prevent .number .trim等、computed计算属性、toFixed方法、reduce数组方法。 涉及的功能需求有&#xff1a;渲染、添加、删除、修改、统计总分&#xff0c;求平均分等。 需求效果如下&#xff1a…

git基础使用--4---git分支和使用

文章目录 git基础使用--4---git分支和使用1. 按顺序看2. 什么是分支3. 分支的基本操作4. 分支的基本操作4.1 查看分支4.2 创建分支4.3 切换分支4.4 合并冲突 git基础使用–4—git分支和使用 1. 按顺序看 -git基础使用–1–版本控制的基本概念 -git基础使用–2–gti的基本概念…