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

news2024/11/26 13:31:51

文章目录

  • 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.coloradmin.cn/o/2124660.html

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

相关文章

C++类和对象3

一.初始化列表 我们之前的构造函数都是在函数体内对数据成员进行赋值 Date(int year, int month, int day) {_year year;_month month;_day day; } 然而我们的构造函数还有另一种初始化的方式&#xff1a;初始化列表 ——初始化列表是以参数表后冒号开始&#xff0c;用数…

数学建模笔记—— 多目标规划

数学建模笔记—— 多目标规划 多目标规划1. 模型原理1.1 多目标规划的一般形式1.2 多目标规划的解1.3 多目标规划的求解 2. 典型例题3. matlab代码实现 多目标规划 多目标规划是数学规划的一个分支。研究多于一个的目标函数在给定区域上的最优化。又称多目标最优化。通常记为 …

VCS(Video Cloud Storage)解决方案研究报告

1.背景 控视频是重要的数据资产和证据链&#xff0c;在银行、交通、司法等行业对视频数据有很高的安全等级。随着监控的重要性不断提升&#xff0c;在能源、电力、校园、厂矿、高星酒店等多场景中对监控存储也有更高要求&#xff0c;体现为海量存储、超长时间和数据安全。为了充…

得物APP助力释放首发经济新活力,解锁年轻潮流密码

在消费升级与高质量发展的时代背景下&#xff0c;我国首发经济正以前所未有的活力蓬勃发展&#xff0c;成为推动市场繁荣、满足个性化消费需求的重要力量。首发&#xff0c;即产品首次在市场亮相&#xff0c;往往代表着最新的设计理念、最尖端的科技应用以及最前沿的潮流趋势。…

C++入门知识(1)

一、namespace 1、用处 可以解决程序里面定义重名变量的问题 namespace是一个命名空间。 定义变量可以在4个域下面定义&#xff0c;全局域&#xff0c;局部域&#xff0c;命名空间域&#xff0c;类域。各个域之间是相互不影响的。命名空间里面的变量可以和外面的变量重名 2…

Stable Diffusion4.9一键安装教程SD(AI绘画软件)

**无套路&#xff01;**文末提供下载方式 Stable Diffusion 是一款革命性的 AI 绘画生成工具&#xff0c;它通过潜在空间扩散模型&#xff0c;将图像生成过程转化为一个逐步去噪的“扩散”过程。 与传统的高维图像空间操作不同&#xff0c;Stable Diffusion 首先将图像压缩到…

样品管理的重要性与实操解决方案,外贸软件一键搞定

在外贸过程中&#xff0c;样品管理是一个重要的环节&#xff0c;它不仅涉及到产品的质量和细节确认&#xff0c;还是与客户沟通的重要桥梁。在选择客户时&#xff0c;通常会优先考虑那些目的明确、意向较强的客户&#xff0c;因为这些客户成交的可能性较大。无论是纺织品、服装…

基于SpringBoot+Vue的学生成绩管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的学生成绩…

Python语法,从入门到精通,一步到位!

Python语法及入门涵盖了多个方面&#xff0c;包括基本语法、数据类型、控制流、函数、模块等。以下是一个超全超详细的介绍&#xff1a; 一、Python基本语法 注释&#xff1a;Python中使用井号&#xff08;#&#xff09;表示注释&#xff0c;从井号开始到行尾的内容都会被Pytho…

一节课教你学会【预处理详解】

谢谢观看&#xff01;希望以下内容帮助到了你&#xff0c;对你起到作用的话&#xff0c;可以一键三连加关注&#xff01;你们的支持是我更新地动力。 因作者水平有限&#xff0c;有错误还请指出&#xff0c;多多包涵&#xff0c;谢谢&#xff01; 预处理详解 一、预定义符号二、…

红米K60U/K50/Note11TPro澎湃OS无法绑定账号解锁BL-不能激活小米账号

小米澎湃OS对于解锁BL&#xff0c;新增了各种限制&#xff0c;早前我们还能使用bypass脚本来实现澎湃OS上绑 定账号成功&#xff0c;但随着澎湃OS七月系统上的推送&#xff0c;旧版的bypass已经彻底失效&#xff0c;并且无法安装 旧版的设置APK来解决问题。此次涉及的机型有红米…

SpringSecurity剖析

1、SpringSecurity 入门 1.1、简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。Spring Security是一个框架&#xff0c;致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样&#xff0c;Sp…

【PX4-AutoPilot教程-TIPS】PX4中MAVLink话题频率修改

PX4中MAVLink话题频率修改 方法一&#xff1a;使用QGC地面站通过命令行解释器MAVLink Shell修改话题频率方法二&#xff1a;使用SD卡中的命令脚本文件修改话题频率方法三&#xff1a;通过修改PX4飞控固件源码修改话题频率 环境&#xff1a; PX4 &#xff1a;1.13.0 方法一&am…

SOP流程制定:vioovi ECRS工时分析软件的智慧引领

在现代制造业中&#xff0c;标准化操作流程&#xff08;SOP&#xff09;已成为提升生产效率、确保产品质量、降低运营成本的关键要素。SOP不仅为生产活动提供了明确的指导&#xff0c;还促进了企业管理的规范化和精细化。然而&#xff0c;如何科学、高效地制定SOP流程&#xff…

CISC 和 RISC 架构的对比

研究 RISC 架构优缺点的最简单方法是将其与其前身进行对比&#xff1a; CISC&#xff08;复杂指令集计算机&#xff09;架构。 内存中的两个数字相乘 右图表示一台普通计算机的存储方案。 主存储器被划分为编号从&#xff08;行&#xff09;1&#xff1a;&#xff08;列&…

RAG系统的7个检索指标:信息检索任务准确性评估指南

大型语言模型&#xff08;LLMs&#xff09;作为一种生成式AI技术&#xff0c;在近两年内获得了显著的关注和应用。但是在实际部署中&#xff0c;LLMs的知识局限性和幻觉问题仍然是一个挑战。检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09…

好网站包含哪些方面

好网站通常在多个方面都表现出色&#xff0c;包括但不限于设计、内容、导航、性能和互动性。下面将详细介绍这些方面。 首先&#xff0c;设计是一个网站吸引用户的第一印象。一个好的网站设计应该是清晰、直观、美观&#xff0c;并且符合用户体验原则。页面布局应该合理&#x…

Spire.PDF for .NET【文档操作】演示:创比较 PDF 文档

PDF 已成为跨不同平台共享和保存文档的标准格式&#xff0c;在专业和个人环境中都发挥着无处不在的作用。但是&#xff0c;创建高质量的 PDF 文档需要多次检查和修订。在这种情况下&#xff0c;了解如何有效地比较 PDF 文件并找出它们的差异变得至关重要&#xff0c;这使文档编…

【Go】Go语言基本语法--注释、变量、常量

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Memfire Cloud使用技巧:让开发更简单,更高效

在软件开发的世界里&#xff0c;Memfire Cloud就像是一位隐形的助手&#xff0c;它悄无声息地帮助开发者们解决了一个又一个难题。如果你还在为搭建服务、开发API接口而头疼&#xff0c;那么Memfire Cloud无疑是你的救星。今天&#xff0c;我们就来聊聊如何使用Memfire Cloud&a…