【JAVA基础】实现Tomcat基本功能

文章目录

  • TCP/IP协议
  • Socket编程
  • Servlet
  • Tomcat


在搜索了两三天之后,也是大概弄懂了Tomcat是个什么东西,我们在说Tomcat之前,先来了解一下下面这三个东西:

TCP/IP协议

TCP/IP 是互联网通信的基础协议。TCP(传输控制协议)负责可靠的数据传输,IP(互联网协议)负责数据包的路由和地址定位。所有的网络通信,包括服务器和客户端之间的通信,都依赖于 TCP/IP 协议。包括五层结构,自上而下分别为:应用层、传输层、网络层、数据链路层、物理层

  • 应用层:发送端想要发送数据,需要应用层准备好要发送的数据,直接与用户进行对接,之后交给传输层
  • 传输层:传输层的主要作用是为发送端和接收端提供可靠的服务,举个例子,就像我们寄送快递时候的物流公司,确保我们的商品不受损坏
  • 网络层:网络层负责选择数据传输的路线,应该走哪个路由器等等
  • 数据链路层:选择好传输路径之后,由数据链路层将数据从一个路由器发送到另外一个路由器
  • 物理层:可以理解为网线等等数据需要走的路线

Socket编程

百度百科上面对Socket的解释是:**套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。**我是这么理解的:Socket编程其实是应用程序进行数据传输的一种方式,它是TCP/IP的一种具体实现,计算机通过Socket协议区分数据应该输送到哪个具体的应用程序,Socket用于监听TCP/IP连接(例如:浏览器中的HTTP请求)

Servlet

Servlet包含于Tomcat中,实质上是一个Java类,是一种运行在支持Java应用服务器上的Web组件,它的作用是响应Socket监听到的HTTP请求,并做出相应处理。其中会包含一个HttpServlet类,是Servlet API的核心类,其自定义的Servlet都是该类的子类。

Tomcat

介绍完上面内容后,我们来看什么是Tomcat:Tomcat是一个基于 Socket 通信的 Java 应用服务器,专门用来运行 Java Servlet,也就是说Tomcat其实是Servlet的容器,它基于Socket通信来获取浏览器中的请求,并做出相应处理,将处理结果交给Servlet,让其做出响应,并将结果再反馈给客户端浏览器。下面是Tomcat运行的基本流程:

在这里插入图片描述

下面写一下代码实现:

我们先定义Servlet类,定义其doGet()doPost()方法以便于对浏览器请求做出响应,service()方法用于判断浏览器的请求类型,并调用对应的方法

// HttpServlet接口 定义了Servlet中的核心方法
@WebServlet(url = "/Http")
public interface HttpServlet {// 接口中定义servlet类中的核心方法void service(Request request);void doGet(Request request) ;void doPost(Request request) ;
}// servlet类 继承接口并对接口中的方法进行实现
@WebServlet(url = "/first")
public class MyFirstServlet implements HttpServlet{// 对传入的请求方法进行判断,属于那种请求,调用响应的doGet或者doPost方法public void service(Request request){if ("GET".equalsIgnoreCase(request.getMethod())) {doGet(request);} else if ("POST".equalsIgnoreCase(request.getMethod())) {doPost(request);}}public void doGet(Request request){System.out.println("hello get1");}public void doPost(Request request){System.out.println("hello post1");}
}@WebServlet(url = "/second")
public class MySecondServlet implements HttpServlet{// 对传入的请求方法进行判断,属于那种请求,调用响应的doGet或者doPost方法public void service(Request request){if ("GET".equalsIgnoreCase(request.getMethod())) {doGet(request);} else if ("POST".equalsIgnoreCase(request.getMethod())) {doPost(request);}}public void doGet(Request request){System.out.println("hello get2");}public void doPost(Request request){System.out.println("hello post2");}
}

定义完Servlet类后,我们来看Tomcat服务器中的核心功能,我们将它分为五步完成:

  1. 扫描Servlet包下面的类,并获取到所有的类的全类名

    要想让Tomcat将获取到的请求数据传输给Servlet类,那就必须先获取到Servlet类的类信息,那么我们对目录进行扫描,并定位到Servlet类所在的文件目录:

    // 定义一个集合用于存储访问地址以及类对象
    static Map<String, Class<?>> servletMaps = new HashMap<>();
    // 定义一个集合用于存储类名
    static List<String> classPaths = new ArrayList<>();public static void main(String[] args) throws IOException {// 查找servlet类名并存储searchClass();
    }// 用于拼接Servlet包所在根目录,并递归进行查找
    private static List<String> searchClass(){String basePack = "servlet"; // 包目录String classPath = MyTomcat.class.getResource("/").getPath(); // 获取文件所在的根目录basePack = basePack.replace(".", File.separator); // 将包名中的 . 替换为路径中的 \// 拼接完整地址String searchPath = classPath + basePack;// 递归通过路径获取类名doPath(new File(searchPath), classPath);// 将最后的集合值返回给main()方法return classPaths;
    }// 递归获取Servlet文件名
    private static void doPath(File file, String classpath) {// 递归条件if (file.isDirectory()) { // 判断是否是文件夹,如果是就继续递归File[] files = file.listFiles();for (File f1 : files) {doPath(f1, classpath);}} else { // 如果是文件,就对名字进行处理,然后存储到classPaths集合中if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/", "\\").replaceFirst("\\\\", ""), "").replace("\\", ".").replace(".class", "");classPaths.add(path);}}
    }
    

    上面的代码中,searchClass()方法用于初始化Servlet包所在的根目录,并调用doPath()方法,对目录进行进一步查找,直到找到我们需要的Servlet类文件

  2. 根据第一步获取到的全类名生成类对象

    创建想对应的类对象之后,我们将类对象以及他的类名,用Key-Value键值对的形式存储到servletMap集合中,方便之后的使用

  3. 获取类上面注解的访问地址

    我们知道浏览器发送的请求是需要Servlet处理的,但是每一个请求都需要调用到对应的Servlet类才可以,所以我们获取到Servlet类上面的注解,用于之后的对比。

    下面是对应的二三步代码:

    // 对存储着Servlet类名的集合进行扫描,创建servlet对象
    for (String className : classPaths) {try {// 创建对应的servlet类对象Class<?> clazz = Class.forName(className);// 获取servlet类上面的注解内容WebServlet webServlet = clazz.getDeclaredAnnotation(WebServlet.class);// 存储在集合中servletMaps.put(webServlet.url(), clazz);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}
    }
    
  4. 如果请求内容和@WebServlet当中的注解是相同的,那么生成Servlet对象

    这一步在实现的时候其实有点曲折,第一次我直接在Tomcat类中想要创建Servlet对象,但是发现Request中存储的请求内容我是没法直接获取到的;于是第二次我又直接在Server类中去创建Servlet对象,但是这里没法获取到Servlet类对象信息;直到这个时候我才反应过来应该在Tomcat中创建阻塞监听,这样才能获取到请求方法,并创建对应的Servlet对象。

    // 定义一个Request对象用于存储请求信息
    static Request request = new Request();// Request类 用于存储请求方法和请求内容
    public class Request {String Method;String Url;public void setMethod(String setMethod) {this.Method = setMethod;}public void setUrl(String setUrl) {this.Url = setUrl;}public String getMethod() {return Method;}public String getUrl() {return Url;}
    }public static void main(String[] args) throws IOException {// 启动tomcat服务器start(4700);
    }// 启动tomcat服务器,其中对端口进行监听,并获取到请求信息
    public static void start(int port) throws IOException {ServerSocket serverSocket = new ServerSocket(port);System.out.println("MyTomcat 启动,监听端口:" + port);while(true) {Socket socket = serverSocket.accept(); // 阻塞监听// 打开输入流,解析客户端发来的内容InputStream inputStream = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); //将 01010这样的bit信息 转换为 字符数据String line = reader.readLine(); // 读取第一行,它包含了请求方法和请求路径等信息if (line != null && !line.isEmpty()){String requestContent = line.split(" ")[1]; // String requestMethod = line.split(" ")[0];request.setUrl(requestContent);request.setMethod(requestMethod);doRequest(request);}}
    }// 对Request类中存储的请求内容和方法进行处理和比对
    public static void doRequest(Request request) {// 存储获取到的请求内容String requestUrl = request.getUrl();for (String servletKey : servletMaps.keySet()){// 判断获取到的请求信息是否和servlet类对象上面的注解名称一样,如果一样那么创建对应类信息if (servletKey.equals(requestUrl)){Class<?> servletClass = servletMaps.get(requestUrl);// System.out.println(servletClass);  做一个输出用于测试是否获取// 如果创建了对应的类对象,那么就开始创建对应的servlet对象,并调用其中的方法if (servletClass != null) {servletClass.newInstance(); // 创建servlet对象} else {System.out.println("404 Not Found: " + requestUrl);}}}
    }
    

    上面的start()方法用于启动tomcat服务器,并监听端口,随时获取浏览器发送的请求,并对请求内容进行处理,将请求方法,和请求内容分别储存在Request类中的对象中,Request类用于存储浏览器中发来的请求,doRequest()方法对获取到的请求方法和请求内容进行处理和比对,并创建Servlet对象

  5. 根据浏览器请求调用Servlet类中的doPost()doGet()方法

    这一步我是搜了好多资料加上老师的提示才写出来的,最后这两步对于我这个初学者实在思考起来有点费劲,需要用到一些代理的知识,因为我们可以发现,在创建完Servlet对象之后,我们没办法对这个对象进行存储,那么我们也就没法通过请求方法进行判断,调用servlet中对应的方法进行响应,我们只能是手动去调用方法。
    那么我们可以使用代理,通过我们创建的HttpServlet接口来存储我们生成的Servlet对象,然后通过httpServlet对象调用service方法,来进行判断,并对请求做出响应。在这里,Tomcat相当于代理对象,Servlet相当于核心对象,通过Tomcat来代理调用到其中的doGet()doPost()核心方法。

    为什么用代理?
    我们可以发现创建的servlet类对象我们只能用Object类来接收,但是如果这样接收的话,我们也就无法调用到Servlet中的doGet等方法
    所以我们让Servlet都继承同一个接口:HttpServlet,这样我们就有办法来存储这个创建的Servlet对象,并在其中判断我们传入的请求方法,进行判断,做出对应的响应。此时Tomcat属于一个代理类

    核心代码其实没多少,但是逻辑我感觉第一次实现的时候不是很好想:

    try {// 通过接口来存储生成的Servlet对象HttpServlet httpServlet = (HttpServlet) servletClass.newInstance();// 假设 Servlet 类中有一个 service() 方法来处理请求,其中对doGet和doPost方法进行调用httpServlet.service(request);
    } catch (Exception e) {e.printStackTrace();
    }
    

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

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

相关文章

SpringBoot框架下的房产销售系统开发

第一章 绪 论 1.1背景及意义 房产销售也都将通过计算机进行整体智能化操作&#xff0c;对于房产销售系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;首页、个人中心、用户管理、销售经理管理、房源信息管理、房源类型管理、房子户型管理、交易订单管…

《Python青少年趣味编程108例》书籍介绍

文章目录 前言为什么选择Python&#xff1f;书籍介绍文章目录配套资源 前言 在这个数字化飞速发展的时代&#xff0c;编程已经成为了一项不可或缺的技能。对于青少年而言&#xff0c;学习编程不仅能够培养逻辑思维、解决问题的能力&#xff0c;还能激发无限创意&#xff0c;让…

【吊打面试官系列-Redis面试题】如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

大家好&#xff0c;我是锋哥。今天分享关于【如果有大量的 key 需要设置同一时间过期&#xff0c;一般需要注意什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 如果有大量的 key 需要设置同一时间过期&#xff0c;一般需要注意什么&#xff1f; 如果大量的…

Windows terminal使用说明

1 terminal基本介绍 1 下载 从微软商店上下载的方式网速比较慢&#xff0c;一种直接的方式是直接用命令行运行命令 winget install --idMicrosoft.WindowsTerminal -e# Window Terminal 安装以及使用(2021最新) 2 ssh配置 # 使用Windows Terminal进行SSH登录 1 通过label…

安泰功率放大器的使用方法及注意事项有哪些

功率放大器是一种用来增加输入信号功率的电子设备。它在各种电子设备和通信系统中被广泛应用&#xff0c;如音响设备、收发器、无线通信设备等。使用功率放大器时&#xff0c;有一些重要的注意事项需要注意&#xff0c;以确保其正常运行并保护设备。 首先&#xff0c;正确的功率…

【数据结构与算法 | 灵神题单 | 分治(链表)篇】力扣148

1. 力扣148&#xff1a;排序链表 1.1 题目&#xff1a; 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4]示例 2&#xff1a; 输入&#xff1a;head [-1,5,3,4…

Unity程序基础框架

概述 单例模式基类 没有继承 MonoBehaviour 继承了 MonoBehaviour 的两种单例模式的写法 缓存池模块 &#xff08;确实挺有用&#xff09; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 缓存池模块 /// 知识点 //…

C++STL~~stackqueue

文章目录 容器适配器一、stack&queue的概念二、stack&queue的使用三、stack&queue的练习四、总结 容器适配器 什么是适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将一个类…

Leetcode 移动零

要求将数组中的所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。下面是该题的 C 解决方案&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int nonZeroPos 0; // 记录非零元素应该放置的位置// 遍历数组&#xff0c;…

【北京迅为】《STM32MP157开发板使用手册》- 第三十章Cortex-M4通用定时器实验

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

相图的科学应用,陶瓷材料创新

陶瓷材料因其优异的物理和化学性能&#xff0c;在航空航天、电子、生物医学等多个领域展现出广阔的应用前景。陶瓷材料的性能很大程度上取决于其微观结构&#xff0c;包括晶粒大小、相组成和分布。相图作为描述陶瓷材料在不同条件下的相变行为和相平衡关系的图表反映了陶瓷材料…

Element-ui el-table 全局表格排序

实现效果如下&#xff1a; 一、当页数据排序 如果只想要当前页面排序&#xff0c;只会涉及到前端&#xff0c;只需在<el-table-column>标签上添加 :sortable"true"即可 二、自定义排序 如果想要全局排序&#xff0c;需要自定义排序函数&#xff0c;请求后台排…

Springboot项目打war包运行及错误解决

一,打war包 1. 修改pom.xml 为了不影响原pom.xml, 我复制了一个文件叫pom_war.xml , 需要打war包就采用pom_war.xml进行打war包, 你也可以直接修改pom.xml ① 打包方式改为war 没有就增加此配置 <packaging>war</packaging> ② 排除内嵌tomcat依赖 <de…

怎样在备忘录中添加提醒?怎么设置备忘录提醒

备忘录作为我们日常生活中常用的软件&#xff0c;其记录事项的便捷性已经得到了广泛认可。无论是工作计划、购物清单还是个人日记&#xff0c;备忘录都能帮助我们将这些信息快速记录下来。然而&#xff0c;如果备忘录能够进一步提供提醒功能&#xff0c;那么它将变得更加实用&a…

122.rk3399 uboot(2017.09) 源码分析2-initf_dm(2024-09-09)

这里接着上一篇来吧&#xff1a; https://blog.csdn.net/zhaozhi0810/article/details/141927053 本文主要是dm_init_and_scan函数的分析&#xff0c;这个内容比较复杂&#xff0c;我也是第一次阅读&#xff0c;错误之处在所难免&#xff0c;请多指教。 uboot的dm框架需要了解…

MyBatis 面试题11-27

11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的? 都有哪些映射形式&#xff1f; Mybatis 在执行 SQL 查询后&#xff0c;会将结果集封装为目标对象并返回。这主要依赖于 Mybatis 的映射机制&#xff0c;它提供了两种主要的映射形式&#xff1a; 第一种&#xff1…

代码质量护航:结合Checkstyle、SpringBoot与Git的最佳实践

在团队开发中&#xff0c;保持一致的代码风格和高质量的代码至关重要。为了提升团队的整体代码质量&#xff0c;防止低质量代码的提交&#xff0c;使用工具对代码进行自动化检查是非常有效的手段之一。在这篇博客中&#xff0c;我将介绍如何通过结合 Checkstyle、Spring Boot 和…

Zotero使用(一)PDF文件导入不会自动识别

上面两种&#xff0c;一种中文&#xff0c;一种英文&#xff0c;会发现&#xff0c;中文的导入进去之后不会自动识别&#xff0c;部分英文也是。不能自动识别就会缺少导出参考文献的功能&#xff0c;怎么办&#xff1f; 发现之前导入喜欢使用PDF格式 可以结合.ris格式&#xf…

网络安全 day5 --- 反弹SHELL不回显带外正反向连接防火墙出入站文件下载

免责声明 本免责声明适用于作者所有文章内容。使用者需明确&#xff0c;网络安全技术仅供学习和合法研究使用&#xff0c;不得用于任何非法活动&#xff0c;如未经授权的入侵、攻击或数据窃取&#xff0c;所有相关法律责任由使用者自行承担。由于网络安全操作可能带来系统崩溃、…

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统 在产品将要上线之前&#xff0c;需要制作不同类型格式的根文件系统 在产品研发阶段&#xff0c;我们还是需要使用nfs的方式挂载根文件系统 优点&#xff1a;可以直接在上位机中修改文件系统内容&#xff0c;延长EMMC的寿命 【1】重启上位机nfs服…