手写tomcat(Ⅲ)——tomcat动态资源的获取

news2024/9/21 4:25:51

仿写tomcat的Servlet接口体系

之前写过一篇博客,Tomcat的Servlet-GenericServlet-HttpServlet体系的具体结构,以及Servlet的生命周期
Servlet讲解

想要模仿tomcat获取动态资源,就需要我们自己仿写一个Servlet接口体系

主要包括:注解,相关的接口+子类,搜索包名下的所有servlet的工具类

MyWebServlet注解

我们首先需要定义一个MyWebServlet注解,仿照了WebServlet注解

import java.lang.annotation.*;

@Target({ElementType.TYPE})// 作用到类
@Retention(RetentionPolicy.RUNTIME)// 运行阶段
public @interface MyWebServlet {

    // 定义url变量,并规定默认值为空字符

    String className() default "";

    String url() default "";
}

相关的接口+子类

MyServlet接口,定义了Servlet的生命周期

import java.io.IOException;

import com.qcby.config.MyServletConfig;

// 自定义的servlet接口
public interface MyServlet {

    void init();

    MyServletConfig getServletConfig();

    String getServletInfo();

    void service(Request request, Response response) throws IOException;

    void destroy();
}

自定义的MyServletConfig类

/**
 * 将获取到的url和classPath径封装到该对象当中
 */
public class MyServletConfig {
    private String url;

    private String classPath;

    public MyServletConfig(String url, String classPath) {
        this.url = url;
        this.classPath = classPath;
    }

    public MyServletConfig() {
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getClassPath() {
        return classPath;
    }

    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }
}

抽象类MyGenericServlet实现了除service以外的方法

import com.qcby.config.MyServletConfig;

public abstract class MyGenericServlet implements MyServlet {


    @Override
    public void init() {

    }

    @Override
    public MyServletConfig getServletConfig() {
        return new MyServletConfig();
    }

    @Override
    public String getServletInfo() {
        return "";
    }

    @Override
    public void destroy() {

    }
}

抽象类MyHttpServlet,实现了service方法,并将其分化为doGet,doPost,doDelete等方法

import java.io.IOException;

public abstract class MyHttpServlet extends MyGenericServlet {
    // 重写service方法,将其分化为不同的方法
    // 抽象类MyHttpServlet没有抽象方法
    @Override
    public void service(Request request, Response response) throws IOException {
        // 判断request的methodType,转发到doGet/doPost当中
        if ("GET".equals(request.getMethodType())) {
            myDoGet(request, response);
        } else if ("POST".equals(request.getMethodType())) {
            myDoPost(request, response);
        }
    }

    public void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {

    }

    public void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {

    }

}

这里的MyHttpServletRequest还有MyHttpServletResponse,是两个接口


public interface MyHttpServletRequest {
}

public interface MyHttpServletResponse {

    void write(String context) throws IOException;
}

分别由Request类和Response类实现

public class Request implements MyHttpServletRequest{

    private String url;

    private String methodType;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethodType() {
        return methodType;
    }

    public void setMethodType(String methodType) {
        this.methodType = methodType;
    }
}

/**
 * 做数据的返回
 */
public class Response implements MyHttpServletResponse {

    // 获取输出流
    private OutputStream outputStream;


    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    // 静态资源的输出

    public void writeHtml(String path) throws Exception {
        //
        // 根据路径返回资源路径地址,例如http://localhost:8666/index.html
        String resource = FileUtil.getResoucePath(path);
        File file = new File(resource);
        if (file.exists()) {
            // 静态资源存在!
            System.out.println("静态资源存在!");
            FileUtil.writeFile(file, outputStream);
        } else {
            System.out.println(path + "对应的该静态资源不存在!");
        }

    }

    // 数据写回
    public void write(String context) throws IOException {
        outputStream.write(context.getBytes());
        outputStream.flush();
    }
}

加载servlet包下的所有servlet,并将其MyWebServlet注解的url值和对应的servlet类对象组装成一个map(最重要)

上面基本都是照猫画葫芦的仿写,其实该实现的方法完全没实现,跟原本的tomcat相比差的比较多

但是这一部分,还原度比较高,tomcat本身也是扫描包,并且反射获取注解的取值,最后组成HashMap来映射servlet的

SearchClassUtil

/**
 * 扫描指定包,获取该包下所有的类的全路径信息
 */
public class SearchClassUtil {
    public static List<String> classPaths = new ArrayList<String>();

    public static List<String> searchClass(){
        //需要扫描的包名
        String basePack = "com.qcby.webapp";
        //将获取到的包名转换为路径
        String classPath = SearchClassUtil.class.getResource("/").getPath();
        basePack =  basePack.replace(".", File.separator);
        String searchPath = classPath + basePack;
        doPath(new File(searchPath),classPath);
        //这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
        return classPaths;
    }

    /**
     * 该方法会得到所有的类,将类的绝对路径写入到classPaths中
     * @param file
     */
    private static void doPath(File file,String classpath) {
        if (file.isDirectory()) {//文件夹
            //文件夹我们就递归
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1,classpath);
            }
        } else {//标准文件
            //标准文件我们就判断是否是class文件
            if (file.getName().endsWith(".class")) {
                String path = file.getPath().replace(classpath.replace("/","\\").
                                replaceFirst("\\\\",""),"").replace("\\",".").
                        replace(".class","");
                //如果是class文件我们就放入我们的集合中。
                classPaths.add(path);
            }
        }
    }

    public static void main(String[] args) {
        List<String> classes = SearchClassUtil.searchClass();
        for (String s: classes
        ) {
            System.out.println(s);
        }
    }
}

tomcat启动类

对静态资源和动态资源做了判断

package com.qcby.tomcatDemo;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLDecoder;

import com.qcby.config.ServletConfigMapping;
import com.qcby.servlet.MyServlet;
import com.qcby.servlet.Request;
import com.qcby.servlet.Response;
import com.qcby.util.ResponseUtil;

/**
 * tomcat启动类
 */
public class TomcatStart {

    private static Request request = new Request();

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        System.out.println("socket服务器启动!!!");
        // 0. 加载servlet类信息
        ServletConfigMapping.init();

        // 1. 打开相关通信端口
        // tomcat:8080,mysql:3306,应用软件独占一个端口的全部信息

        ServerSocket serverSocket = new ServerSocket(8666);
        // 线程持续扫描当前网卡xxxx端口(死循环),如果有数据就拿过来,交给端口对应的程序处理
        // 2. 监听并接收请求数据
        while (true) {
            // 一旦发现有数据,就打开socket通信
            // 这里没有创建新的线程,所以这里是main线程监听数据
            Socket socket = serverSocket.accept();
            System.out.println(socket.getInetAddress().getCanonicalHostName() + "进行了连接!");

            // 第二步监听并接收到了数据,处理数据可以用主线程,但是没必要,创建子线程处理
            // 每接收一次数据,创建一个子线程
            Thread t1 = new Thread(() -> {
                // 处理数据包括两部分:读和写
                try {
                    dataHandle(socket);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

            });
            t1.start();
        }
    }

    // 处理数据的方法,读+写
    public static void dataHandle(Socket socket) throws Exception {
        // 1. 读取请求的数据
        // 1.1打开输入流对象,读取socket对象中的数据,这里的数据都是0101010的二进制数据
        InputStream inputStream = socket.getInputStream();
        requestContext(inputStream);

        // 数据的输出
        Response response = new Response(socket.getOutputStream());
        if ("".equals(request.getUrl())) {
            response.write(ResponseUtil._404ResponseHeader);
        } else if (ServletConfigMapping.classMap.get(request.getUrl()) != null) {
        	// 动态web资源
            dispatch(request, response);
        } else {
            // 访问静态资源
            response.writeHtml(request.getUrl());
        }
    }

    public static void dispatch(Request request, Response response) throws InstantiationException, IllegalAccessException, IOException {
        Class<MyServlet> servletClass = ServletConfigMapping.classMap.get(request.getUrl());
        if (servletClass != null) {
            MyServlet myServlet = servletClass.newInstance();
            myServlet.service(request, response);
        }
    }

    public static void requestContext(InputStream inputStream) throws IOException {

        //  1.2 二进制数据的翻译并读取
        int count = 0;
        while (count == 0) {
            // 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
            // 下一个调用可能是同一个线程,也可能是另一个线程。
            // 一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。


            // 可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回 0
            count = inputStream.available();
        }
        byte[] bytes = new byte[count];

        inputStream.read(bytes);

        // 这里用URLDecoder是为了防止路径中出现特殊符号,经过get请求之后会被URLEncode为乱码
        String context = URLDecoder.decode(new String(bytes, "utf-8"));// Base64.getDecoder().decode(bytes)
        System.out.println("===context:" + context);

        if ("".equals(context)) {
            System.out.println("null request!");
            request.setUrl("");
            request.setMethodType("");
        } else {
            //根据换行来获取第一行数据
            String firstLine = context.split("\\n")[0];
            // 第一行数据的第2个字符串
            System.out.println("===url:" + firstLine.split("\\s")[1]);
            request.setUrl(firstLine.split("\\s")[1]);
            // 第一行数据的第1个字符串
            System.out.println("===methodType:" + firstLine.split("\\s")[0]);
            request.setMethodType(firstLine.split("\\s")[0]);
        }
    }
}

webapp包下有两个servlet
在这里插入图片描述

@MyWebServlet(url = "/first")
public class FirstServlet extends MyHttpServlet {

    @Override
    public void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {
        String context = "<h1>这是FirstServlet的myDoGet方法</h1>";

        resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));
    }

    @Override
    public void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {
        String context = "<h1>这是FirstServlet的myDoPost方法</h1>";
        resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));
    }
}
@MyWebServlet(url = "/hello")
public class HelloServlet extends MyHttpServlet {
    @Override
    public void myDoGet(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {
        String context = "<h1>这是HelloServlet的myDoGet方法</h1>";
        resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));
    }

    @Override
    public void myDoPost(MyHttpServletRequest req, MyHttpServletResponse resp) throws IOException {
        String context = "<h1>这是HelloServlet的myDoPost方法</h1>";
        resp.write(ResponseUtil.makeResponse(context, ResponseUtil.htmlResponseHeader));
    }
}

项目下还有一个静态资源index.html
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>Hello TomcatDemo!!!</p>
</body>
</html>

我们在浏览器分别测试静态和动态资源的获取

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

第15篇:JTAG UART IP应用<二>

Q&#xff1a;如何通过C库函数访问JTAG UART&#xff1f; A&#xff1a;创建Quartus硬件工程以及Platform Designer系统&#xff0c;这与我们之前创建的第一个Nios II工程--Hello_World的Quartus硬件工程一样&#xff0c;IP组件有Clock Source、Nios II Professor、On-Chip Me…

机器重启oracle数据库自动启动异常

业务连接报ora-01033&#xff1a; 检查服务器&#xff0c;发现有重启记录&#xff0c;oracle进程存在&#xff0c;数据库状态处于mount状态&#xff0c;检查日志&#xff0c;发现在数据库启动时没有正常open&#xff1a; 连入数据库&#xff1a;Select * from v$backup 发现数…

SpringBoot集成Logback将日志写入文件夹

一、logback简介&#xff1a; 目前比较常用的ava日志框架:Logback、log4j、log4j2、JUL等等。 Logback是在log4j的基础上重新开发的一套日志框架&#xff0c;是完全实现SLF4J接口API(也叫日志门面)。 Logback 的架构非常通用&#xff0c;可以应用于不同的环境。目前logback分为…

数据分析项目有哪些实施流程?揭示从数据准备到解决方案全过程

在当今数据驱动的商业环境中&#xff0c;数据分析项目的成功实施对于企业洞察市场趋势、优化产品服务、提升用户体验以及增强竞争力具有至关重要的作用。特别是对于直播类应用软件这样的快速增长领域&#xff0c;如何通过数据分析来扩大付费用户基础、提高用户留存率&#xff0…

OceanBase的存储架构与传统LSM-Tree架构的异同|OceanBase数据转储合并技术解读(二)

前篇博文将OceanBase的存储架构巧妙地与自然界中的“水生态”进行了类比&#xff0c;今日我们转变视角&#xff0c;聚焦在与拥有相同LSM-Tree架构的其他产品的比较&#xff0c;深入探讨OceanBase相较于它们所展现出的独特性能。 众所周知&#xff0c;OceanBase数据库的存储引擎…

aws eks理解和使用podidentity为pod授权

参考链接 https://www.amazonaws.cn/new/2024/amazon-eks-introduces-eks-pod-identity/https://aws.amazon.com/cn/blogs/aws/amazon-eks-pod-identity-simplifies-iam-permissions-for-applications-on-amazon-eks-clusters/ 先决条件 集群版本需要符合要求&#xff0c;如果…

数据结构~~链式二叉树

目录 一、基本概念 链式存储概念 二、链式二叉树的结构 链式二叉树结构 构建链式二叉树 二叉树的遍历 二叉树节点和高度等 二叉树销毁 三、链式二叉树的练习 相同的树 对称二叉树 另外一颗子树 二叉树前序遍历 二叉树遍历 四、完整代码 Tree.h Tree.c 五、总结 一…

.哈希表.

哈希 哈希表&#xff1a;将大而复杂的数据映射到紧凑的区间内。分为&#xff1a;①存储结构 &#xff08;离散化是特殊的哈希&#xff0c;之前讲的离散化是严格保序的 映射到区间上是连续递增的&#xff09; 哈希不保序&#xff0c;这里讲的是一般的哈希 弊端&#xff1a;若…

【Spring】认识 Spring AOP

认识 Spring AOP 1.什么是 AOP2.AOP 中的概念3.用 AOP 方式管理日志3.1 编写 AOP 日志注解类3.2 编写控制器用于测试 1.什么是 AOP AOP&#xff08;Aspect Oriented Program&#xff0c;面向切面编程&#xff09;把业务功能分为核心、非核心两部分。 核心业务功能&#xff1a…

Spark-RDD-依赖关系详解

Spark概述 Spark-RDD概述 Spark-RDD-依赖关系 在Apache Spark中&#xff0c;RDD&#xff08;Resilient Distributed Dataset&#xff09;是一种基本的抽象数据结构&#xff0c;代表了分布式的、不可变的数据集。 RDD之间的依赖关系在Spark中非常重要&#xff0c;因为它们决定了…

YOLO 学习和使用 (重拾机器学习)

contents a nenrons 单层神经网络 多层神经网络 CNN (Convolutional Neural Network) YOLO 5.1. YOLO(you only look once) 5.2. predict stage: 置信度 * 类别条件概率 全概率非极大值抑制&#xff0c;通过IOU 指数进行实现每个 grid cell 生成两个预测 bounding box 无…

4. C++网络编程-TCP客户端的实现

TCP Client网络编程基本步骤 创建socket&#xff0c;指定使用TCP协议使用connect连接服务器使用recv/send接收/发送数据关闭socket TCP-connect连接请求 !man 2 connect #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sock…

Java面试题--JVM大厂篇(1-10)

引言&#xff1a; 在这个信息时代&#xff0c;对于准备进入大厂工作的朋友们来说&#xff0c;对于JVM&#xff08;Java虚拟机&#xff09;的掌握是面试中的一项重要内容。下面是一些精选的JVM面试题&#xff0c;希望对大家能有所帮助。 正文&#xff1a; 1. JVM有哪几种垃圾收…

车道线识别与预警系统LDWS(代码+教程)

车道线识别与预警系统&#xff08;Lane Departure Warning System, LDWS&#xff09;作为智能交通系统中的重要组成部分&#xff0c;旨在通过先进的图像处理和计算机视觉技术&#xff0c;实时监测车辆行驶过程中的车道位置&#xff0c;预防因驾驶员疏忽或疲劳导致的车道偏离事故…

自己手写了一个大模型RAG项目-06.使用自己的embedding模型

大家好&#xff0c;我是程序锅。 github上的代码封装程度高&#xff0c;不利于小白学习入门。 常规的大模型RAG框架有langchain等&#xff0c;但是langchain等框架源码理解困难&#xff0c;debug源码上手难度大。 因此&#xff0c;我写了一个人人都能看懂、人人都能修改的大…

Unity入门理论+实践篇之Luna

创建世界的主角 父子物体 首先创建一个cube物体 可以观察到其在2D视角下的坐标为&#xff08;0&#xff0c;0&#xff09; 此时将cube物体拖拽到ldle_0下&#xff0c;如图所示&#xff0c;并将其坐标值改为&#xff08;2&#xff0c;2&#xff09; 此时再将ldle_0物体的坐标…

拓数派入选中电联大数据与统计分会两大重点专项工作组

自中国电力企业联合会大数据与统计分会成立以来&#xff0c;深入贯彻党中央、国务院关于不断做强做优做大我国数字经济有关要求&#xff0c;充分发挥数据要素乘数效应&#xff0c;凝聚行业专家及能源电力产业链各主体力量&#xff0c;持续推进能源电力数据资源交易共享&#xf…

Unity环绕物体的摄像机,添加了遮挡的适应

第三人人称摄像机 支持的功能 设定目标后使用鼠标可以环绕目标点旋转&#xff0c;且会进行遮挡的适配&#xff0c;当有遮挡的时候会移动差值移动到没有遮挡的位置。 使用方式 将vThirdPersonCamera 挂在与摄像机上然后为target赋值。 如果有需要检测遮挡的层级可以修改&…

数据仓库与数据挖掘实验练习6-7(实验四2024.5.22)

tips&#xff1a; 列出虚拟环境&#xff1a;conda env list 激活虚拟环境&#xff1a;activate hi 进入jupyter-lab&#xff1a;jupyter lab 练习6 1. 处理字符串空格 发现问题: 使用 values 属性查看数据时&#xff0c;如果发现 Name 列没有对齐&#xff0c;很可能是 Name 左…

2024年软考总结 信息系统管理师

选择题 英文题&#xff0c;我是一题也没把握&#xff0c;虽然我理解意思。 千万不要认为考死记硬背不对。目的不在于这。工程项目中有很多重要的数字&#xff0c;能记住说明你合格。 案例 几乎把答案全写在案例中了。 计算题 今年最简单。没有考成本。 只考了关键路径&a…