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

server/2024/12/20 12:06:20/

目录

一、什么是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/server/151697.html

相关文章

三分钟完成vue项目中iconfont图标的导入

iconfont 提供了丰富的图标库&#xff0c;也允许个人上传分享图标&#xff0c;非常复合中文视觉体验。平时的开发中也离不开iconfont图标的使用&#xff0c;今天就给大家聊一下iconfont图表在vue项目中的快速导入&#xff01; 1、添加图标 登录iconfont-阿里巴巴矢量图标库&a…

springboot446数字化农家乐管理平台的设计与实现(论文+源码)_kaic

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#x…

vscode+msys2+clang+xmake c++开发环境搭建

转载请标明出处&#xff1a;小帆的帆的专栏 安装msys2 下载msys2安装包&#xff1a;清华源下载地址安装msys2&#xff1a;安装目录&#xff0c;C:\Softwares\msys64 安装cling工具链&#xff0c;xmake &#xff01;&#xff01;&#xff01;在开始菜单中启动MSYS2 CLANG64,…

打造卓越技术文档的艺术:从清晰表达到团队协作的桥梁

在现代软件开发与技术创新的进程中&#xff0c;技术文档不仅仅是描述代码或系统架构的工具&#xff0c;更是团队之间沟通、协作与知识传承的关键媒介。对于技术人员来说&#xff0c;良好的文档往往能有效降低沟通成本&#xff0c;提升开发效率&#xff0c;并帮助新成员快速了解…

Chaos Mesh云原生的混沌测试平台搭建

Chaos Mesh云原生的混沌测试平台搭建 一.环境准备 ​ 确认已经安装helm&#xff0c;如要查看 Helm 是否已经安装&#xff0c;请执行如下命令&#xff1a; helm version二.使用helm安装 1.添加 Chaos Mesh 仓库 ​ 在 Helm 仓库中添加 Chaos Mesh 仓库&#xff1a; helm re…

rpc设计的再次思考20251215(以xdb为核心构建游戏框架)

1.服务提供者注册的方式 // 表明这是一个服务提供者&#xff0c;ServerType 和 ServerId从application.properties中读取 // 而且只有当当前服务是Game时&#xff0c;才生效。 或者 条件注解??? RpcProvider(typeServerType.Game) public class GameProvider{MsgReceiver…

比较器基本玩法

1 红外水龙头 1 比较器的工作特性 2 红外接收二极管的工作原理 1 红外发射二极管和红外接收二极管 2 红外接收二极管接收到&#xff0c;产生的电流是100uA&#xff0c;即电压等于4V 3 自动出水水龙头 4 比较器内部结构 1 有一些比较器需要加上拉电阻&#xff08;类似开漏输…

加速访问github与下载

浏览加速 将github.com替换为下列任意一个 dgithub.xyz github.site github.store github下载文件资源加速 公共 CDN 静态资源加速服务 | 7ED https://www.7ed.net/start/public-cdn.html 将github原网址拼接于下列网址后面