Tomcat原理(5)——tomcat最终实现

embedded/2024/12/20 14:35:09/

目录

一、什么是Servlet容器

二、ServletConfigMapping构建实现容器

ServletConfigMapping

MyTomcat

三、优化server

Server

 MyTomcat

四、匹配

代码如下:

测试如下:


上一篇博客已经为介绍了servelet的实现 ,这篇对上一篇博客进行补充,实现如下流程

一、什么是Servlet容器

        就像上一篇博客说的动态资源映射表,Servlet容器就是一个Key—Value集合。在MyTomcat中我们获取到了注解值Key和类对象的路径。

二、ServletConfigMapping构建实现容器

  • 我通过public static Map<String,Class<HttpServlet>> classMap=new HashMap<>();来定义我们的容器结构。
  • 因为我想让获取key-value信息在我打开tomcat时就加载好,我这里应用了static代码块。代码块会在方法执行前初始化。
  • 在最后再定义一个init方法(为空),我们只需要在Mytomcat主体文件中调用这个方法,就可以完成初始化动作
ServletConfigMapping
java">package com.qcby.tomcat.config;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.webservlet.WebServlet;import java.io.File;
import java.net.URL;
import java.util.*;/*
* servlet容器
* */
public class ServletConfigMapping {//注意这写HttpServlet,父类对象,因为它要封装一系列子类对象public static Map<String,Class<HttpServlet>> classMap=new HashMap<>();//static代码块在main方法之前执行static {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());System.out.println(webServlet.path());classMap.put(webServlet.path(), (Class<HttpServlet>) clazz);}}} catch (Exception e) {e.printStackTrace();}}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;}public static void init(){}
}
MyTomcat
java">package com.qcby.tomcat;import com.qcby.tomcat.config.ServletConfigMapping;public class MyTomcat {public static void main(String[] args) {//调用ServletConfigMappingServletConfigMapping.init();}}

调用MyTomcat的main方法

至此,流程实现了一半

 

三、优化server

我们在上一篇博客中单独实现了server的用法,我们现在把这个文件代码优化,并合并到MyTomcat当中

Server

我们把main方法更改为public static void serverInit(),效果和上面ServletConfigMapping是类似的的我们只需要在MyTomcat中调用serverInit()方法,即可实现server服务的打开

java">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 serverInit() throws IOException {// 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);}}
}
 MyTomcat
java">package com.qcby.tomcat;import com.qcby.tomcat.config.ServletConfigMapping;
import com.qcby.tomcat.socket.Server;import java.io.IOException;public class MyTomcat {public static void main(String[] args) {try {//调用ServletConfigMapping,初始化servlet容器ServletConfigMapping.init();//启动server服务Server.serverInit();} catch (IOException e) {e.printStackTrace();}}}

至此,流程又实现了一半

 

四、匹配

        完成上面二三步骤,我们就相当于既得到了servlet容器,又识别出来了http请求要找的对象。现在就是怎么将二者匹配起来。

        在上一篇博客中,我们把所受到的http请求的method和path都封装在了request对象中,所以我们通过调取request进行连接

代码如下:

  如下代码中我把server放在了MyTomcat中。

java">package com.qcby.tomcat;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.config.ServletConfigMapping;
import com.qcby.tomcat.socket.Server;
import com.qcby.tomcat.webservlet.WebServlet;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;public class MyTomcat {public static Request request=new Request();public static Response response=new Response();public static void main(String[] args) throws Exception {ServletConfigMapping.init();serverInit();}public static void serverInit() throws Exception{// 1.打开通信端口   tomcat:8080   3306  ---------》进行网络通信ServerSocket serverSocket = new ServerSocket(8080);//监听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, InstantiationException, IllegalAccessException {//将bit流转为文字信息int count = 0;while (count == 0){count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);String Context = new String(bytes);System.out.println(Context);//解析数据if(Context.equals("")){System.out.println("你输入了一个空请求");}else {//获得第一行的前两个String firstLine=Context.split("\\n")[0];//根据换行来获取第一行数据String path=firstLine.split("\\s")[1];String method=firstLine.split("\\s")[0];System.out.println(path+" "+method);request.setMethod(method);request.setPath(path);}dis(request);}public static void dis(Request request) throws InstantiationException, IllegalAccessException {if(!request.getPath().equals("")){//不是空请求if(ServletConfigMapping.classMap.get(request.getPath())!=null){//当前请求地址能否从classMap中查找到Class<HttpServlet> ClassServlet=ServletConfigMapping.classMap.get(request.getPath());//获取类对象HttpServlet servlet=ClassServlet.newInstance();//根据获取到的类对象,创建对应的对象  用父类去接,多态(父类的引用指向子类的对象)servlet.service(request,response);//调用HttpServlet的service方法,判断是get请求还是post请求}}}}
java"> public static void dis(Request request) throws InstantiationException, IllegalAccessException {if(!request.getPath().equals("")){//不是空请求if(ServletConfigMapping.classMap.get(request.getPath())!=null){//当前请求地址能否从classMap中查找到Class<HttpServlet> ClassServlet=ServletConfigMapping.classMap.get(request.getPath());//获取类对象HttpServlet servlet=ClassServlet.newInstance();//根据获取到的类对象,创建对应的对象  用父类去接,多态(父类的引用指向子类的对象)servlet.service(request,response);//调用HttpServlet的service方法,判断是get请求还是post请求}}}

 注意!!HttpServlet servlet=ClassServlet.newInstance();是根据获取到的类对象,创建对应的对象  用父类去接,多态(父类的引用指向子类的对象)

方法是对象当中的一块内存空间,这是类对象,不是对象,想要得到信息就得生成对象。

这一句是通过类对象去创建对象。但是因为不确定是请求的哪个对象,需要用父类去承接(多态), 根据对象调用它的doGet或doPost方法。

        整个流程这就走通了。首先当我们启动之后,我们就已经创建好了Servlet的map集合,此时用户的HTTP请求打过来。现在我们处理请求信息,把这些请求信息封装在request对象当中。获取到类对象后,根据获取到的类对象,创建对应的对象。然后再去调用方法。至此成功实现某Servlet的doGet方法。

测试如下:
java">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) {System.out.println("你好我是FirstServlet");}@Overridepublic void doPost(Request request, Response response) {}
}

 


http://www.ppmy.cn/embedded/147303.html

相关文章

路径规划之启发式算法之十九:混合蛙跳算法(Shuffled Frog Leaping Algorithm,SFLA)

混合蛙跳算法(Shuffled Frog Leaping Algorithm,SFLA)是一种基于群体智能的优化算法,它受到青蛙捕食行为的启发,并融合了基于模因(meme)进化的思想和群体智能优化的优点。这种算法最初由Eusuff和Lansey在2003年提出,用于解决多峰值优化问题,特别是在全局最优解和局部最…

理解支持向量机

支持向量机属于机器学习 支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种典型的机器学习算法&#xff0c;属于监督学习范畴。它主要用于分类问题&#xff0c;也可以用于回归问题。在机器学习的众多算法中&#xff0c;SVM以其在小样本、高维空间…

shell5

字符串运算符 首先我们在终端利用vim打开u.sh str1"hello" str2"hello" if [ "$str1" "$str2" ]; thenecho True elseecho false fi我们把hello改为Hello&#xff0c;看一下大小写是否敏感 str1"Hello" str2"hello…

CSS padding(填充)

CSS padding&#xff08;填充&#xff09; 概述 CSS&#xff08;层叠样式表&#xff09;中的padding属性用于设置元素的内边距&#xff0c;即元素内容与边框之间的空间。这个属性对于控制页面布局和元素间距至关重要。本文将详细介绍padding属性的使用方法、值、单位以及如何…

第六章:反射+设计模式

一、反射 1. 反射 (Reflection) &#xff1a;允许在程序运行状态中&#xff0c;可以获取任意类中的属性和方法&#xff0c;并且可以操作任意对象内部的属 性和方法&#xff0c;这种动态获取类的信息及动态操作对象的属性和方法对应的机制称为反射机制。 2. 类对象 和 类的…

[一招过] Python的正则表达式篇

Python 正则表达式&#xff08;re模块&#xff09; 正则表达式&#xff08;regular expression&#xff09;是用于匹配字符串的一种强大工具。Python 提供了 re 模块来处理正则表达式。通过正则表达式&#xff0c;可以快速匹配、查找、替换、分割字符串等。 1. re 模块基础 …

Selenium之execute_script()方法执行js脚本

目录 场景应用和使用 页面滚动 获取返回值 返回JavaScript定位的元素对象 修改元素属性 弹出提示框 场景应用和使用 在自动化测试中&#xff0c;部分场景无法使用自动化Selenium原生方法来进行测试&#xff1a; 滚动到某个元素&#xff08;位置&#xff09; 修改…

【C语言】库函数常见的陷阱与缺陷(一):字符串处理函数[2]--gets函数

C语言中的gets函数是一个用于从标准输入(通常是键盘)读取一行字符串的函数。然而,gets函数存在多个陷阱与缺陷,这些缺陷可能导致程序崩溃、安全漏洞或未定义行为。 一、gets功能与用法 gets函数的主要作用是从标准输入(通常是键盘)读取一行字符串,并将其存储在指定的缓…