手写Tomcat

ops/2025/3/10 11:37:08/
  1. Tomcat是什么?

Tomcat 是一个开源的、轻量级的 Servlet 容器,也被称为 Web 服务器,由 Apache 软件基金会的 Jakarta 项目开发,在 Java Web 开发领域应用广泛

1)Servlet 容器:Servlet 是 Java 语言编写的服务器端程序,Tomcat 负责管理 Servlet 的生命周期,接收客户端的请求,将请求分发给相应的 Servlet 进行处理,并将 Servlet 处理后的结果返回给客户端。

2)Web 服务器:它能够处理 HTTP 请求,支持静态资源(如 HTML、CSS、JavaScript 文件等)的访问,也能动态生成网页内容。

如下为其图标)

 

2.Tomcat结构

  1. bin目录存 tomcat基本命令
  2. conf目录放置配置文件
  3. lib目录主要用来存放tomcat运行需要加载的jar包
  4. logs目录存放主动启动tomcat的日志
  5. temp目录存放临时文件
  6. Work目录存放 tomcat编译时产生的文件
  7. webapps目录存放我们写的web项目

3.手写Tomcat

手写模拟 Tomcat 程序旨在接收客户端的 HTTP 请求,依据请求的 URL 找到对应的 Servlet 进行处理,并将处理结果以 HTTP 响应的形式返回给客户端。程序通过多个类、接口和注解的协同运作来达成这一功能。

4.在IDEA里手写Tomcat模拟程序的步骤:

1)首先在IDEA里建一个新项目,选择Maven项目(Maven 提供了强大的依赖管理功能,然后在src包中的java包中再创建com.qcby软件包。(如下图)

2)在IDEA里写一个tomcat模拟程序,建一个新项目之后首先建了一个com.qcby软件包 然后在com.qcby包中建了Util,webapps两个包和mytomcat,servletconfigmapping两个类,在util中有responseutil,searchclassutil两个类和webservlet两个注解,在在webapps包中还有三个软件包分别是myweb,req和servlet,myweb中有两个类loginservlet和showservlet,req包中有两个httpservletrequest和httpservletresponse类,在servlet包中有generikservlet,httpservlet两个类和一个servlet接口。(如下图)

5.类、接口和注解的详细信息及关系

1. 注解:WebServlet

  • 定义位置com.qcby.Utill.WebServlet
  • 作用:用于标记一个类为 Servlet 类,并指定该 Servlet 映射的 URL 路径。此为自定义注解,模仿了 Java Servlet 规范中的@WebServlet注解。
  • 与其他组件的关系:被ServletConfigMapping类用于扫描和识别 Servlet 类及其对应的 URL 映射。
  • 代码示例

java

package com.qcby.Utill;

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

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface WebServlet {

    String value();}

2. 工具类:SearchClassUtil

  • 定义位置com.qcby.Utill.SearchClassUtil
  • 作用:扫描指定包下的所有类,找出使用了WebServlet注解的类。借助 Java 的反射机制和类加载器来实现类的扫描与查找。
  • 与其他组件的关系:为ServletConfigMapping类提供服务,帮助其找到所有被注解的 Servlet 类。
  • 代码示例

java

package com.qcby.Utill;

import java.io.File;import java.io.IOException;import java.net.URL;import java.util.ArrayList;import java.util.Enumeration;import java.util.List;

public class SearchClassUtil {

    public static List<Class<?>> scanClasses(String packageName) throws IOException, ClassNotFoundException {

        List<Class<?>> classes = new ArrayList<>();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        String path = packageName.replace('.', '/');

        Enumeration<URL> resources = classLoader.getResources(path);

        while (resources.hasMoreElements()) {

            URL resource = resources.nextElement();

            File directory = new File(resource.getFile());

            if (directory.exists()) {

                findClasses(directory, packageName, classes);

            }

        }

        return classes;

    }

    private static void findClasses(File directory, String packageName, List<Class<?>> classes) throws ClassNotFoundException {

        File[] files = directory.listFiles();

        if (files != null) {

            for (File file : files) {

                if (file.isDirectory()) {

                    findClasses(file, packageName + "." + file.getName(), classes);

                } else if (file.getName().endsWith(".class")) {

                    String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);

                    classes.add(Class.forName(className));

                }

            }

        }

    }

    public static List<Class<?>> findAnnotatedClasses(String packageName) throws IOException, ClassNotFoundException {

        List<Class<?>> allClasses = scanClasses(packageName);

        List<Class<?>> annotatedClasses = new ArrayList<>();

        for (Class<?> clazz : allClasses) {

            if (clazz.isAnnotationPresent(WebServlet.class)) {

                annotatedClasses.add(clazz);

            }

        }

        return annotatedClasses;

    }}

3. 接口:Servlet

  • 定义位置com.qcby.webapps.servlet.Servlet
  • 作用:定义 Servlet 的基本生命周期方法,如init(初始化)、service(处理请求)和destroy(销毁)。所有的 Servlet 类都需要实现这个接口。
  • 与其他组件的关系GenericServletHttpServlet都实现了这个接口,为具体的 Servlet 类提供统一的规范。
  • 代码示例

java

package com.qcby.webapps.servlet;

import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;

public interface Servlet {

    void init();

    void service(HttpServletRequest request, HttpServletResponse response) throws IOException;

    void destroy();}

4. 抽象类:GenericServlet

  • 定义位置com.qcby.webapps.servlet.GenericServlet
  • 作用:实现了Servlet接口,提供了一些通用的 Servlet 功能和方法的默认实现。它是一个抽象类,为具体的 Servlet 类提供了一个基础框架。
  • 与其他组件的关系:继承自Servlet接口,HttpServlet继承自GenericServlet
  • 代码示例

java

package com.qcby.webapps.servlet;

import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;

public abstract class GenericServlet implements Servlet {

    @Override

    public void init() {

        System.out.println("初始化servlet....");

    }

    @Override

    public abstract void service(HttpServletRequest request, HttpServletResponse response) throws IOException;

    @Override

    public void destroy() {

        System.out.println("实现servlet对象的销毁....");

    }}

5. 具体类:HttpServlet

  • 定义位置com.qcby.webapps.servlet.HttpServlet
  • 作用:继承自GenericServlet,专门用于处理 HTTP 请求。它可以根据不同的 HTTP 请求方法(如 GET、POST)进行不同的处理。
  • 与其他组件的关系:继承自GenericServlet,具体的 Servlet 类(如LoginServletShowServlet)可以继承HttpServlet类来处理 HTTP 请求。
  • 代码示例

java

package com.qcby.webapps.servlet;

import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;

public abstract class HttpServlet extends GenericServlet {

    @Override

    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String method = request.getMethod();

        if ("GET".equalsIgnoreCase(method)) {

            doGet(request, response);

        } else if ("POST".equalsIgnoreCase(method)) {

            doPost(request, response);

        }

    }

    protected abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException;

    protected abstract void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException;}

6. 请求和响应类:HttpServletRequest 和 HttpServletResponse

  • 定义位置com.qcby.webapps.req.HttpServletRequest 和 com.qcby.webapps.req.HttpServletResponse
  • 作用HttpServletRequest封装了客户端的 HTTP 请求信息,如请求方法、请求路径、请求参数等;HttpServletResponse封装了服务器的响应信息,用于向客户端返回响应内容。
  • 与其他组件的关系:在Servlet接口的service方法中作为参数传递,供具体的 Servlet 类处理请求和生成响应。
  • 代码示例

java

// HttpServletRequestpackage com.qcby.webapps.req;

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

public class HttpServletRequest {

    private String path;

    private String method;

    private Map<String, String> parameters = new HashMap<>();

    public HttpServletRequest() {

    }

    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;

    }

    public void setParameter(String name, String value) {

        parameters.put(name, value);

    }

    public String getParameter(String name) {

        return parameters.get(name);

    }

    public void parseParameters(String requestBody) {

        if (requestBody != null && !requestBody.isEmpty()) {

            String[] pairs = requestBody.split("&");

            for (String pair : pairs) {

                String[] keyValue = pair.split("=");

                if (keyValue.length == 2) {

                    setParameter(keyValue[0], keyValue[1]);

                }

            }

        }

    }}

// HttpServletResponsepackage com.qcby.webapps.req;

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

public class HttpServletResponse {

    private OutputStream outputStream;

    public HttpServletResponse(OutputStream outputStream) {

        this.outputStream = outputStream;

    }

    public void sendResponse(String s) throws IOException {

        String response = "HTTP/1.1 200 OK\r\n" +

                "Content-Type: text/html\r\n" +

                "Content-Length: " + s.length() + "\r\n" +

                "\r\n" +

                s;

        outputStream.write(response.getBytes());

        outputStream.flush();

    }}

7. 配置管理类:ServletConfigMapping

  • 定义位置com.qcby.ServletConfigMapping
  • 作用:管理 Servlet 的配置信息,将 Servlet 类和其映射的 URL 路径进行关联。它利用SearchClassUtil扫描指定包下的 Servlet 类,并根据WebServlet注解的信息建立映射关系。
  • 与其他组件的关系:依赖SearchClassUtil来获取 URL 路径被注解的 Servlet 类,依赖WebServlet注解来获取 URL 路径,为MyTomcat提供 Servlet 映射信息。
  • 代码示例

java

package com.qcby;

import com.qcby.Utill.SearchClassUtil;import com.qcby.Utill.WebServlet;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.List;import java.util.Map;

public class ServletConfigMapping {

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

    public static void init(String packageName) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        List<Class<?>> annotatedClasses = SearchClassUtil.findAnnotatedClasses(packageName);

        for (Class<?> clazz : annotatedClasses) {

            WebServlet webServlet = clazz.getAnnotation(WebServlet.class);

            if (webServlet != null) {

                String urlPattern = webServlet.value();

                HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();

                servletMap.put(urlPattern, servlet);

            }

        }

    }}

8. 具体 Servlet 类:LoginServlet 和 ShowServlet

  • 定义位置com.qcby.webapps.myweb.LoginServlet 和 com.qcby.webapps.myweb.ShowServlet
  • 作用:具体的业务 Servlet 类,继承自HttpServlet,实现具体的业务逻辑。
  • 与其他组件的关系:继承自HttpServlet,通过WebServlet注解的 URL 路径与特定的 URL 进行映射,当客户端请求该 URL 时,对应的 Servlet 类的service方法会被调用进行处理。
  • 代码示例

java

// LoginServletpackage com.qcby.webapps.myweb;

import com.qcby.Utill.WebServlet;import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;

@WebServlet("/login")public class LoginServlet extends HttpServlet {

    @Override

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {

        try {

            response.sendResponse("<html><body><h1>Hello from LoginServlet (GET)</h1></body></html>");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    @Override

    protected void doPost(HttpServletRequest request, HttpServletResponse response) {

        try {

            response.sendResponse("<html><body><h1>Hello from LoginServlet (POST)</h1></body></html>");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }}

// ShowServletpackage com.qcby.webapps.myweb;

import com.qcby.Utill.WebServlet;import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;

@WebServlet("/show")public class ShowServlet extends HttpServlet {

    @Override

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {

        try {

            response.sendResponse("<html><body><h1>Hello from ShowServlet (GET)</h1></body></html>");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    @Override

    protected void doPost(HttpServletRequest request, HttpServletResponse response) {

        try {

            response.sendResponse("<html><body><h1>Hello from ShowServlet (POST)</h1></body></html>");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }}

9. 核心类:MyTomcat

  • 定义位置com.qcby.MyTomcat
  • 作用:模拟 Tomcat 服务器的核心类,包括启动服务器、接收客户端请求、根据请求的 URL 路径从ServletConfigMapping中找到对应的 Servlet,并调用该 Servlet 的service方法进行请求处理和响应生成。
  • 与其他组件的关系:依赖ServletConfigMapping类获取 Servlet 映射信息,接收HttpServletRequestHttpServletResponse对象进行请求处理和响应返回。
  • 代码示例

java

package com.qcby;

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

public class MyTomcat {

    public static void main(String[] args) {

        try {

            ServletConfigMapping.init("com.qcby.webapps.myweb");

            ServerSocket serverSocket = new ServerSocket(8081);

            System.out.println("MyTomcat started on port 8081");

            while (true) {

                Socket socket = serverSocket.accept();

                InputStream inputStream = socket.getInputStream();

                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[] lines = context.split("\\r\\n");

                    String firstLine = lines[0];

                    String[] parts = firstLine.split(" ");

                    String method = parts[0];

                    String uri = parts[1];

                    HttpServletRequest request = new HttpServletRequest();

                    request.setPath(uri);

                    request.setMethod(method);

                    String requestBody = null;

                    boolean isBody = false;

                    StringBuilder bodyBuilder = new StringBuilder();

                    for (String line : lines) {

                        if (isBody) {

                            bodyBuilder.append(line);

                        }

                        if (line.isEmpty()) {

                            isBody = true;

                        }

                    }

                    if (bodyBuilder.length() > 0) {

                        requestBody = bodyBuilder.toString();

                        request.parseParameters(requestBody);

                    }

                    HttpServletResponse response = new HttpServletResponse(socket.getOutputStream());

                    if (ServletConfigMapping.servletMap.containsKey(request.getPath())) {

                        HttpServlet servlet = ServletConfigMapping.servletMap.get(request.getPath());

                        servlet.service(request, response);

                    } else {

                        try {

                            response.sendResponse("<html><body><h1>404 Not Found</h1></body></html>");

                        } catch (IOException e) {

                            e.printStackTrace();

                        }

                    }

                }

                socket.close();

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }}

关系总结

  • GenericServlet实现了Servlet接口。
  • HttpServlet继承自GenericServlet
  • LoginServletShowServlet继承自HttpServlet
  • ServletConfigMapping依赖SearchClassUtil查找 Servlet 类,依赖WebServlet注解获取 URL 映射。
  • MyTomcat依赖ServletConfigMapping获取 Servlet 映射关系,接收HttpServletRequestHttpServletResponse进行请求处理和响应。
  • WebServlet注解用于LoginServletShowServlet,实现 URL 与 Servlet 类的映射 。

6.

Tomcat 虚拟机工作原理

这个模拟 Tomcat 程序的工作原理可以概括为以下几个步骤:

  1. 启动服务器MyTomcat 类的 main 方法创建一个 ServerSocket 对象,并监听指定的端口(这里是 8081)。
  2. 初始化 Servlet 映射:调用 ServletConfigMapping.init 方法,通过 SearchClassUtil 扫描指定包下的类,找出带有 WebServlet 注解的类,将注解中的 value 属性作为 URL 路径,创建对应的 Servlet 实例,并将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
  3. 接受客户端请求ServerSocket 持续监听客户端的连接请求,当有客户端连接时,通过 accept 方法获取一个 Socket 对象,从该对象的输入流中读取客户端发送的 HTTP 请求信息。
  4. 解析请求信息:将读取到的请求信息解析为 HttpServletRequest 对象,提取请求方法(如 GET、POST)和请求路径。
  5. 查找对应的 Servlet:根据请求路径在 servletMap 中查找对应的 Servlet 实例。
  6. 处理请求:如果找到对应的 Servlet 实例,调用其 service 方法处理请求。HttpServlet 类的 service 方法会根据请求方法调用 doGet 或 doPost 方法。
  7. 发送响应:在 doGet 或 doPost 方法中,根据业务逻辑生成响应内容,通过 HttpServletResponse 对象将响应信息发送给客户端。
  8. 关闭连接:处理完请求后,关闭 Socket 连接,继续监听下一个客户端请求。

类之间的逻辑关系、映射和继承关系

继承关系

  • GenericServlet 实现了 Servlet 接口,提供了 init 和 destroy 方法的默认实现,service 方法为抽象方法,需要子类实现。
  • HttpServlet 继承自 GenericServlet,重写了 service 方法,根据请求方法调用 doGet 或 doPost 方法,doGet 和 doPost 方法为抽象方法,需要具体的 Servlet 类实现。
  • LoginServlet 和 ShowServlet 继承自 HttpServlet,实现了 doGet 和 doPost 方法,处理具体的业务逻辑。
映射关系

  • WebServlet 注解用于将 Servlet 类映射到一个 URL 路径,ServletConfigMapping 类通过扫描带有该注解的类,将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
逻辑关系

  • MyTomcat 类是程序的入口,负责启动服务器、接受客户端请求、解析请求信息、查找对应的 Servlet 实例并调用其 service 方法处理请求。
  • ServletConfigMapping 类负责初始化 Servlet 映射,通过 SearchClassUtil 扫描指定包下的类,找出带有 WebServlet 注解的类,创建对应的 Servlet 实例,并将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
  • SearchClassUtil 类用于扫描指定包下的类,找出带有 WebServlet 注解的类。
  • HttpServletRequest 类用于封装客户端的请求信息,包括请求方法、请求路径和请求参数。
  • HttpServletResponse 类用于封装服务器的响应信息,提供了 sendResponse 方法将响应信息发送给客户端。
  • ResponseUtil 类提供了一些工具方法,用于生成 HTTP 响应头信息。

7.在tomcat中servlet是动态资源;网页前端代码是静态资源

手写完tomcat之后需要配置动态资源和静态资源,才能收到请求

这里以收到servlet的动态资源为重点

配置完动态资源之后在网页搜索localhost://(在MyTomcat定义的端口)8081/show  就会在网页得到请求(如下)

这种情况表明动态资源配置成功了


http://www.ppmy.cn/ops/164647.html

相关文章

02C#基本结构篇(D1_基本语法)

目录 一、前言 二、using 关键字 三、class 关键字 四、C# 中的注释 五、成员变量 六、成员函数 七、实例化一个类 八、标识符 九、C# 关键字 十一、顶级语句&#xff08;Top-Level Statements&#xff09; 实例一 实例二 实例三 注意事项 十二、Java与C#的语法区…

10.2 继承与多态

文章目录 继承多态 继承 继承的作用是代码复用。派生类自动获得基类的除私有成员外的一切。基类描述一般特性&#xff0c;派生类提供更丰富的属性和行为。在构造派生类时&#xff0c;其基类构造函数先被调用&#xff0c;然后是派生类构造函数。在析构时顺序刚好相反。 // 基类…

【AVRCP】协议深入解析(1):从框架到数据单元

目录 一、AVRCP 协议框架 1.1 AV/C 命令 1.2 AVRCP 特定的 AV/C 命令 1.3 AVRCP 特定的浏览命令 二、定时器设置 三、协议数据单元(PDU)描述 3.1 PDU 格式概述 3.2 AVRCP 特定的 AV/C PDU 格式 3.3 AVRCP 特定的浏览 PDU 格式 四、总结 五、参考文献 AVRCP(Audio…

让知识触手可及!基于Neo4j的机械设备知识图谱问答系统

让知识触手可及&#xff01;基于Neo4j的机械设备知识图谱问答系统 在信息化迅速发展的今天&#xff0c;我们如何高效地利用海量数据&#xff1f;我们推出的“机械设备知识图谱问答展示系统”&#xff0c;正是为了解决这个难题而生。这个系统不仅能帮助我们快速获取机械设备的知…

介绍一下Qt中的动态属性

在 Qt 中&#xff0c;动态属性是一种强大且灵活的特性&#xff0c;它允许你在运行时为对象添加、修改和查询属性&#xff0c;而不需要在类的定义中预先声明这些属性。下面为你详细介绍 Qt 动态属性的相关内容&#xff1a; 1. 动态属性的基本概念 在传统的 C 类中&#xff0c;属…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

Linux 配置静态 IP

一、简介 在 Linux CentOS 系统中默认动态分配 IP 地址&#xff0c;每次启动虚拟机服务都是不一样的 IP&#xff0c;因此要配置静态 IP 地址避免每次都发生变化&#xff0c;下面将介绍配置静态 IP 的详细步骤。 首先先理解一下动态 IP 和静态 IP 的概念&#xff1a; 动态 IP…

DeepSeek 开源周回顾「GitHub 热点速览」

上周&#xff0c;DeepSeek 发布的开源项目用一个词形容就是&#xff1a;榨干性能&#xff01;由于篇幅有限&#xff0c;这里仅列出项目名称和简介&#xff0c;感兴趣的同学可以前往 DeepSeek 的开源组织页面&#xff0c;深入探索每个项目的精彩之处&#xff01; 第一天 FlashML…