当前位置: 首页 > article >正文

手写Tomcat

手写Tomcat

  • Tomcat详解
  • 划分结构
  • 详解结构
  • 代码示例
    • req
      • HttpServletRequest
      • HttpServletResponse
    • Servlet 接口
    • GenericServlet 抽象类
    • HttpServlet 抽象类
    • Util 工具包
      • ResponseUtil
      • SearchClassUtil
      • Webservlet 注解
    • webapps.myweb
      • LoginServlet
      • ShowServlet
    • ServletConfigMapping
    • MyTomcat

手写一个简单的 Tomcat 部分,涉及到 Servlet 容器的实现,虽然无法做到完整地模拟 Tomcat 的复杂性,但可以通过简化的代码示例来展示 Tomcat 作为 Servlet 容器的核心概念和工作原理。

Tomcat 的关键任务是接收 HTTP 请求、路由到相应的 Servlet 进行处理,并将响应返回给客户端。

Tomcat详解

划分结构

首先我们进行划分结构,如下图:
在这里插入图片描述

详解结构

  1. servlet – HttpServlet – GenericServlet 表示了Servlet的继承实现关系,标志着Servlet的声明周期
  2. req 中的HttpServletRequest 和 HttpServletResponse 代表着 request 和 response 请求
  3. Util 是工具包
  4. webapps.myweb 表示的是自己的web项目
  5. 最后是Tomcat 和 Servlet容器的构建

代码示例

req

HttpServletRequest

public class HttpServletRequest {
    private String method;
    private String path;

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

HttpServletResponse

import java.io.IOException;
import java.io.OutputStream;

public class HttpServletResponse {
    private OutputStream outputStream;

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

    public void writeServlet(String context) throws IOException {
        outputStream.write(context.getBytes());
    }

}

Servlet 接口

import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;

/**
 * 定义了servlet的生命周期
 */

public interface Servlet {
    void init(); // 初始化servlet
    void service(HttpServletRequest request, HttpServletResponse response) throws Exception; // 服务
    void destroy();  // 销毁
}

GenericServlet 抽象类

public abstract class GenericServlet implements Servlet{

    public void init() {
        System.out.println("初始化servlet------");
    }


    public void destroy() {
        System.out.println("销毁servlet------");
    }
}

HttpServlet 抽象类

import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;


public abstract class HttpServlet extends GenericServlet{
    public void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if(request.getMethod().equals("GET")){
            doGet(request,response);
        }else if(request.getMethod().equals("POST")){
            doPost(request,response);
        }
    }

    public abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception;
    public abstract void doPost(HttpServletRequest request, HttpServletResponse response)throws Exception;
}

Util 工具包

ResponseUtil

public class ResponseUtil {
    public  static  final String responseHeader200 = "HTTP/1.1 200 \r\n"+
            "Content-Type:text/html; charset=utf-8 \r\n"+"\r\n";

    public static String getResponseHeader404(){
        return "HTTP/1.1 404 \r\n"+
                "Content-Type:text/html; charset=utf-8 \r\n"+"\r\n" + "404";
    }

    public static String getResponseHeader200(String context){
        return "HTTP/1.1 200 \r\n"+
                "Content-Type:text/html; charset=utf-8 \r\n"+"\r\n" + context;
    }
}

SearchClassUtil

/**'
 * 扫描com.qcby.webapps目录下面的文件,获取每一个Java文件的全类名
 */
public class SearchClassUtil {
    public static List<String> classPaths = new ArrayList<String>();

    public static List<String> searchClass(){
        //需要扫描的包名
        String basePack = "com.qcby.webapps";
        //将获取到的包名转换为路径
        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);
        }
    }
}

Webservlet 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)  // 表明注解的生命周期,SOURCE:源代码,CLASS:编译成class文件,RUNTIME:运行时   源文件时就有效
@Target(ElementType.TYPE)   // 表明@Webservlet注解只能用于类上
public @interface Webservlet {
//    String path = null;

    String urlMapping() default ""; // 自定义的servlet路径
}

webapps.myweb

随便给出两个web项目

LoginServlet

import com.qcby.Util.ResponseUtil;
import com.qcby.Util.Webservlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;

import com.qcby.servlet.HttpServlet;

import java.io.IOException;

@Webservlet(urlMapping = "/Login")
public class LoginServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("我是Login");
        response.writeServlet(ResponseUtil.getResponseHeader200("hello 你好"));
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) {

    }
}

ShowServlet

import com.qcby.Util.Webservlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;

import com.qcby.servlet.HttpServlet;

@Webservlet(urlMapping = "/show")
public class ShowServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {

    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) {

    }
}

ServletConfigMapping

import com.qcby.Util.SearchClassUtil;
import com.qcby.Util.Webservlet;
import com.qcby.servlet.HttpServlet;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * servlet 容器
 */

public class ServletConfigMapping {

    // 扫描com.qcby.webapps包下的所有类,并获取它的全类名
    // 根据全路径名获取类对象,获取@Webservlet注解,获取urlMapping属性值
    // 创建servlet容器,将path和对象写入容器当中

    public static Map<String, HttpServlet> servletMap = new HashMap<>();

    // 将path和对象写入容器中
    public static void init() throws Exception {
        List<String> classPath = SearchClassUtil.searchClass();
        for(String path: classPath){
            getMessage(path);
        }
    }



    // 利用反射获取类的注释信息
    public static void getMessage(String classPath) throws Exception {
        Class clazz = Class.forName(classPath);
        // 注释对象
        Webservlet webservlet = (Webservlet) clazz.getDeclaredAnnotation(Webservlet.class);
        HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();
        servletMap.put(webservlet.urlMapping(),servlet);


    }
}

MyTomcat

import com.qcby.servlet.HttpServlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class MyTomcat {

    static HttpServletRequest request = new HttpServletRequest();

    public static void main(String[] args) throws Exception {
        ServletConfigMapping.init();
        //1.创建serversocket对象,持续监听8080端口
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true){
            //accept():阻塞监听  ,当代码执行到这一样,如果没有数据到来,那么循环阻塞在这里。如果有数据到来,就继续向下执行
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            HttpServletResponse response = new HttpServletResponse(outputStream);

            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]; //根据换行来获取第一行数据
                request.setPath(firstLine.split("\\s")[1]);
                request.setMethod(firstLine.split("\\s")[0]);
            }

            // 根据访问而定路径为key值从map集合当中尝试获取数据
            if(ServletConfigMapping.servletMap.containsKey(request.getPath())){
                HttpServlet servlet = ServletConfigMapping.servletMap.get(request.getPath());
                servlet.service(request,response);
            }

        }
    }

}

http://www.kler.cn/a/580851.html

相关文章:

  • C++ 内存模型
  • 从头开始开发基于虹软SDK的人脸识别考勤系统(python+RTSP开源)(三)
  • 1688商品列表商品详情API接口全面解析
  • upload-labs详解(13-20)文件上传分析
  • 大湾区经济网战略媒体澳门《红刊》访霍英东集团
  • 转自南京日报:天洑软件创新AI+仿真技术变制造为“智造
  • 从C#中的MemberwiseClone()浅拷贝说起
  • CentOS7离线部署安装Dify
  • 网络安全技术整体架构 一个中心三重防护
  • 基于架构的软件开发(ABSD)
  • 3D模型语义搜索引擎
  • 聚水潭数据集成到MySQL的高效方法
  • 51c视觉~3D~合集2
  • 笔记本电脑外接固态移动硬盘可以用于深度学习吗
  • Cryptography 与 PyCryptodome 源码级解析
  • MCP-代码解读TypeScript版本
  • (二分 数学推导区间 两个数组的距离值)leetcode 1385
  • 【第21节】C++设计模式(行为模式)-Chain of Responsibility(责任链)模式
  • Redis7——进阶篇(五)
  • Consensus 大会全观察:政策、生态与技术交汇,香港能否抢占 Web3 先机?