Tomcat原理(4)——尝试手动Servlet的实现

目录

一、什么是Servlet

1.servlet的定义

2.servlet的结构

二、实现servlet的流程图

三、具体实现代码

1、server

 2.实体类request&response

3.HttpServlet抽象类

4.再定义三个servlet进行测试 


 

Tomcat原理(3)——静&动态资源以及运行项目的基本流程-CSDN博客文章浏览阅读414次,点赞2次,收藏2次。Tomcat原理(2)——注解及注解的实现-CSDN博客(1)注解一般用于对程序的说明,就像注释一样,但是区别是注释是给人看的,但是注解是给程序看的。(2)让编译器进行编译检查的作用,比如下边这个@Override注解是重写的意思,子类重写了父类的方法,但是改动了方法名,所以报错。https://blog.csdn.net/2301_78566776/article/details/144508887?spm=1001.2014.3001.5502 我们在上一篇博客中已经了解到了静动态资源和tomcat的基本流程

一、什么是Servlet

1.servlet的定义

        Servlet,全称为Java Servlet,是运行在Java服务器端的程序,它主要用于接收和响应来自客户端基于HTTP协议的请求。Servlet可以看作是在服务器上运行的小程序,用于生成动态Web内容。

2.servlet的结构

这是一个servlet的基本结构。我们观察可以发现:

  • Servlet继承了一个HttpServlet抽象类
  • servlet文件包含两个主要的方法:doGet和doPost
  • 观察这两个方法的入参一个是request 一个是response

二、实现servlet的流程图

1.我们首先需要构造一个Server服务器模拟,来接受HTTP请求,并且返回method(get/post)

2.创建request实体类和respons实体类,在request实体类中设置method和path变量

3.创建HttpServlet抽象类,在类里定义doGet和doPost两个抽象方法,并且判断所传method为什么类型,进行选择执行。

4.具体执行的doGet和doPost方法,要在具体的servlet中执行(找抽象类的具体实现方法)——即我们每一个servlet都要对doGet和doPost进行重写。

如图所示

 

三、具体实现代码

引入

我们尝试接收整个http请求的信息

package com.qcby.tomcat.webservlet;/*
* 服务器端 tomcat ——》接收信息
* */import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class SocketServer {public static void main(String[] args) throws IOException {run();}public static void run() throws IOException {ServerSocket serverSocket= new ServerSocket(8080);//端口的范围:0~65535while (true){//等待客户端连接Socket socket = serverSocket.accept();//阻塞监听:程序会在这里卡住。只有监听到客户端的信息后才会向下执行//输出客户端给我们发来的程序InputStream inputStream=socket.getInputStream();//打开输入流:接收输入的信息int count=0;while (count==0){count=inputStream.available();}byte[] bytes=new byte[count];//01010101010100001101010 用字节数组接收inputStream.read(bytes);String context=new String(bytes);System.out.println(context);}}
}

结果如图

我们可以看到这个请求的全部信息。第一行的前两个词为其method/path,在一会的server文件中我们只要前两个词。

1、server

        这个类中我们获取到了http请求的method(get/post)和path(@WebServlet中的path)

获取的方法是对所有请求信息进行切割。并且在最后我们将method和path放入了request中

package com.qcby.tomcat.socket;import com.qcby.tomcat.Request.Request;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {private static Request request=new Request();public static void main(String[] args) throws Exception {// 1.打开通信端口   tomcat:8080   3306  ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(8080);System.out.println("****************server start.....");//2.接受请求数据while (true) {Socket socket = serverSocket.accept();  //--------------------->注意:此时监听网卡的是:主线程System.out.println("有客户进行了链接");new Thread(() -> {//处理数据---------》数据的处理在于读和写try {handler(socket);} catch (Exception e) {e.printStackTrace();}}).start();}}public static void handler(Socket socket) throws Exception {//读取请求的数据InputStream inputStream = socket.getInputStream();requestContext(inputStream);}public static void requestContext(InputStream inputStream) throws IOException {// 创建一个StringBuilder对象,用于构建请求的第一行StringBuilder sb = new StringBuilder();int context; // 用于存储每次从输入流中读取的单个字节// 读取输入流直到遇到换行符(\n)或文件结束(-1)while ((context = inputStream.read()) != -1) {// 如果读取到换行符,则停止读取if (context == '\n') {break; // 遇到换行符,退出循环}// 将读取到的字节转换为字符,并添加到StringBuilder中sb.append((char) context);}// 从StringBuilder中获取第一行字符串,并去除首尾空格String firstLine = sb.toString().trim();// 检查第一行是否为空if (firstLine.isEmpty()) {// 如果为空,则打印提示信息System.out.println("你输入了一个空请求");} else {// 如果不为空,则按空格分割第一行字符串为单词数组String[] words = firstLine.split("\\s+");// 打印出请求方法和请求URI(通常是数组的前两个元素)// 注意:这里没有检查数组长度,如果数组长度小于2,将会抛出ArrayIndexOutOfBoundsException// 在实际应用中,应该添加适当的错误处理或验证逻辑String method=words[0];String path=words[2];System.out.println(words[0] + " " + words[1]);request.setMethod(method);request.setPath(path);}}
}

@WebServlet接口

package com.qcby.tomcat.webservlet;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface WebServlet {String path() default "";
}

 

 2.实体类request&response

生成了get和set方法

生成了全参和无参的构造函数

package com.qcby.tomcat.Request;public class Request {private String path;private String method;public Request(String path, String method) {this.path = path;this.method = method;}public Request() {}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}

 在这里 response我并没有实现

package com.qcby.tomcat.Response;public class Response {
}
3.HttpServlet抽象类

抽象类中既可以有抽象方法也可以有具体方法。

package com.qcby.tomcat.HttpServlet;import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;public abstract class HttpServlet {public  void service(Request request, Response response){if(request.getMethod().equals("GET")){doGet(request,response);}else if (request.getMethod().equals("POST")){doPost(request,response);}else if (request.getMethod().equals("")){System.out.println("未获取到方法类型");}}public abstract void doGet(Request request,Response response);public abstract void doPost(Request request,Response response);}
4.再定义三个servlet进行测试 

注意!每个servlet都有继承HttpServlet,并且在每个servlet中都要重写doGet和doPost

package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="myFirstServlet")
public class MyFirstServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}
package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="mySecondServlet")
public class MySecondServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}

 

package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="myThirdServlet")
public class MyThirdServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}

我们会发现它已经很像Java的servlet文件模板了 

补充:

如何读取一个软件包,遍历里面的servlet,返回其类名和注解的path值

package com.qcby.tomcat;import com.qcby.tomcat.webservlet.WebServlet;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;public class MyTomcat {public static void main(String[] args) {try {// 1. 扫描包路径 (com.wzh.tomcat.myweb)String packageName = "com.qcby.tomcat.MyWeb";List<Class<?>> classes = getClasses(packageName);// 2. 遍历所有类,检查是否有@WebServlet注解for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {// 3. 获取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.path());}}} catch (Exception e) {e.printStackTrace();}}/*** 获取指定包下的所有类** @param packageName 包名,例如 "com.wzh.tomcat.myweb"* @return 类对象列表* @throws Exception*/private static List<Class<?>> getClasses(String packageName) throws Exception {List<Class<?>> classes = new ArrayList<>();String path = packageName.replace('.', '/'); // 将包名转换为文件路径// 通过类加载器获取包的资源路径ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Enumeration<URL> resources = classLoader.getResources(path);while (resources.hasMoreElements()) {URL resource = resources.nextElement();File directory = new File(resource.toURI());// 扫描文件夹下的所有类文件if (directory.exists()) {for (File file : directory.listFiles()) {if (file.getName().endsWith(".class")) {// 获取类的完整类名String className = packageName + "." + file.getName().replace(".class", "");classes.add(Class.forName(className));}}}}return classes;}
}

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

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

相关文章

Node.js内置模块

1.内置模块 Node.js的中文网参考手册:https://nodejs.cn//api 帮助文档 API文档:查看对应的模块,左边是模块,右边是模块的成员 源码:https://github.com/nodejs/node/tree/main/lib 查看 例如: http.js 创建web服务器的模块 -->进入源码中,搜索…

【RAG实战】RAG与大模型应用

1.1 大模型应用的方向&#xff1a;RAG 1.1.1 什么是RAG 1. 生成式AI 一种能够生成各类内容的技术&#xff0c;包括文本、图像、音频和合成数据。自2022年底ChatGPT在全球范围内推广以来&#xff0c;基于Transformer解码器结构的大模型已能在短时间内为用户生成高质量的文本、…

基于DeepSpeed Chat详解 PPO 算法中的actor_loss_fn及其核心参数

详解 PPO 算法中的 actor_loss_fn 及其核心参数 1. 引言 在强化学习中&#xff0c;PPO&#xff08;Proximal Policy Optimization&#xff0c;近端策略优化&#xff09;算法是一种经典且高效的策略优化方法。它通过重要性采样&#xff08;Importance Sampling&#xff09;和策…

D3 基础1

D3 D3.js (Data-Driven Documents) 是一个基于 JavaScript 的库&#xff0c;用于生成动态、交互式数据可视化。它通过操作文档对象模型 (DOM) 来生成数据驱动的图形。官方网站是 https://d3js.org/ <!DOCTYPE html> <html lang"en"><head><me…

基线检查:Windows安全基线.【手动 || 自动】

基线定义 基线通常指配置和管理系统的详细描述&#xff0c;或者说是最低的安全要求&#xff0c;它包括服务和应用程序设置、操作系统组件的配置、权限和权利分配、管理规则等。 基线检查内容 主要包括账号配置安全、口令配置安全、授权配置、日志配置、IP通信配置等方面内容&…

Python -- Linux中的Matplotlib图中无法显示中文 (中文为方框)

目的 用matplotlib生成的图中文无法正常显示 方法 主要原因: 没找到字体 进入windows系统的C:\Windows\Fonts目录, 复制自己想要的字体 粘贴到Linux服务器中对应python文件所处的文件夹内 设置字体: 设置好字体文件的路径在需要对字体设置的地方设置字体 效果 中文正常显…

快速理解类的加载过程

当程序主动使用某个类时&#xff0c;如果该类还未加载到内存中&#xff0c;则系统会通过如下三个步骤来对该类进行初始化&#xff1a; 1.加载&#xff1a;将class文件字节码内容加载到内存中&#xff0c;并将这些静态数据转换成方法区的运行时数据结构&#xff0c;然后生成一个…

搭建 Elasticsearch 集群:完整教程

本文将详细介绍如何在 Linux 环境下搭建一个 Elasticsearch 集群&#xff0c;涵盖环境准备、配置优化、服务启动等多个环节。 一、环境准备 创建安装目录 mkdir /es cd /es解压 Elasticsearch 安装包 tar -xzf elasticsearch-7.10.1-linux-x86_64.tar.gz -C /es配置环境变量 编…

宝塔-docker拉取宝塔镜像,并运行宝塔镜像

宝塔-拉取宝塔镜像&#xff0c;并运行镜像 第1步&#xff1a;查询 docker search btpanel/baota此docker镜像由堡塔安全官方发布&#xff0c;镜像版本为宝塔面板9.2.0正式版和9.0.0_lts 稳定版&#xff0c;镜像会随着宝塔面板更新。 目前支持x86_64和arm架构可供下载使用 版本…

使用 Valgrind 检测 C 程序中的内存问题 -基础教程

内存泄漏是许多 C 语言程序中的常见问题&#xff0c;它不仅会导致程序性能下降&#xff0c;甚至可能让系统崩溃。为了检测和修复这些问题&#xff0c;Valgrind 是一个非常强大的工具&#xff0c;它可以帮助我们分析 C 程序中的内存使用情况&#xff0c;检测内存泄漏、越界访问、…

穷举vs暴搜vs深搜vs回溯vs剪枝专题一>子集

题目&#xff1a; 两个方法本质就是决策树的画法不同 方法一解析&#xff1a; 代码&#xff1a; class Solution {private List<List<Integer>> ret;//返回结果private List<Integer> path;//记录路径&#xff0c;注意返回现场public List<List<Int…

leecode双指针部分题目

leecode双指针部分题目 1. 验证回文串2. 判断子序列3. 两数之和 II - 输入有序数组4. 盛最多水的容器5. 三数之和 1. 验证回文串 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 …

Web 应用如何使用sqlite?使用 sql.js 实现前端 SQLite 数据库操作

前言 在 Web 应用开发中&#xff0c;前端数据处理的重要性日益增加。为了实现更高效的前端数据管理&#xff0c;特别是在处理结构化数据时&#xff0c;sql.js 提供了一个出色的解决方案。sql.js 是将 SQLite 数据库编译为 JavaScript 的库&#xff0c;允许开发者在浏览器环境中…

docker 安装 mysql8.0容器外无法连接

文章目录 概要问题描述解决方案其他命令 概要 主要是mysql5.7和mysql8.0的兼容性问题。 排查了很久 其实就是配置文件的一句话的事情 感觉mysql8.0更为严谨 这样可能是考虑杜绝一些漏洞吧 问题描述 在容器内 netstat -an | grep 3306 都不行 在容器外 netstat -an | grep 2…

TCP协议简单分析和握手挥手过程

TCP介绍 TCP是可靠的传输层协议&#xff0c;建立连接之前会经历3次握手的阶段。 确认机制&#xff1a;接受方 收到数据之后会向 发送方 回复ACK重传机制&#xff1a;发送方 在一定时间内没有收到 接收方的ACK就会重新发送 握手目的&#xff1a;与端口建立连接 TCP的三次握手 …

VisualStudio vsix插件自动加载

本文介绍如何在Visual Studio扩展中实现PackageRegistration&#xff0c;包括设置UseManagedResourcesOnly为true&#xff0c;允许背景加载&#xff0c;并针对C#、VB、F#项目提供自动装载&#xff0c;附官方文档链接。增加以下特性即可…… [PackageRegistration(UseManagedRe…

opencv所有常见函数

一、opencv图像操作 二、opencv图像的数值运算 三、opencv图像的放射变换 四、opencv空间域图像滤波 五、图像灰度化与直方图 六、形态学图像处理 七、阈值处理与边缘检测 八、轮廓和模式匹配

【Excel】单元格分列

目录 分列&#xff08;新手友好&#xff09; 1. 选中需要分列的单元格后&#xff0c;选择 【数据】选项卡下的【分列】功能。 2. 按照分列向导提示选择适合的分列方式。 3. 分好就是这个样子 智能分列&#xff08;进阶&#xff09; 高级分列 Tips&#xff1a; 新手推荐基…

【STM32练习】基于STM32的PM2.5环境监测系统

一.项目背景 最近为了完成老师交付的任务&#xff0c;遂重制了一下小项目用STM32做一个小型的环境监测系统。 项目整体示意框图如下&#xff1a; 二.器件选择 单片机&#xff08;STM32F103&#xff09;数字温湿度模块&#xff08;DHT11&#xff09;液晶显示模块&#xff08;0.8…

《开源数据:开启信息共享与创新的宝藏之门》

《开源数据&#xff1a;开启信息共享与创新的宝藏之门》 一、开源数据概述&#xff08;一&#xff09;开源数据的定义&#xff08;二&#xff09;开源数据的发展历程 二、开源数据的优势&#xff08;一&#xff09;成本效益优势&#xff08;二&#xff09;灵活性与可定制性&…