手写tomcat框架

目录

1、servlet的加载流程

1、Servlet加载流程:

​编辑

[2、Servlet 的加载(生命周期)原理](#2、Servlet 的加载(生命周期)原理)

3、为什么要这么设计?

2、tomcat执行流程

[步骤 1:请求到达服务器](#步骤 1:请求到达服务器)

[步骤 2:Tomcat 通过 Socket 建立连接](#步骤 2:Tomcat 通过 Socket 建立连接)

[步骤 3:解析 HTTP 请求数据](#步骤 3:解析 HTTP 请求数据)

[步骤 4:Servlet 容器匹配对应的 Servlet 对象](#步骤 4:Servlet 容器匹配对应的 Servlet 对象)

[步骤 5:调用 Servlet 的业务方法处理请求](#步骤 5:调用 Servlet 的业务方法处理请求)

[步骤 6:返回响应](#步骤 6:返回响应)

[1. BIO(Blocking IO,阻塞 IO)](#1. BIO(Blocking IO,阻塞 IO))

[2. NIO(Non-Blocking IO,非阻塞 IO)](#2. NIO(Non-Blocking IO,非阻塞 IO))

[3. AIO(Asynchronous IO,异步 IO)](#3. AIO(Asynchronous IO,异步 IO))

对比总结

3、手写tomcat思路流程

[3.1 启动阶段:初始化 Servlet 容器](#3.1 启动阶段:初始化 Servlet 容器)

[3.2 请求处理阶段:接收请求→处理→响应](#3.2 请求处理阶段:接收请求→处理→响应)

[3.3 核心本质](#3.3 核心本质)

4、具体手写tomcat

[4.1 首先在idea中创建maven项目](#4.1 首先在idea中创建maven项目)

[4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。](#4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。)

[4.3 在webapps里面创建MyfirstServlet和MysecondServlet。](#4.3 在webapps里面创建MyfirstServlet和MysecondServlet。)

[4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承​​​​​GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。](#4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。)

[4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。](#4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。)

[4.6 这是我们考虑到了第三步@WebServlet注解。](#4.6 这是我们考虑到了第三步@WebServlet注解。)

[4.7 接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。](#4.7 接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。)

[4.7.3需要注意的是: 1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型)](#4.7.3需要注意的是: 1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型))

1、servlet的加载流程

1、Servlet加载流程:

Servlet 的实现是 "接口→抽象类→具体实现" 的逐层简化流程:

  1. Servlet 接口 :定义了 Servlet 的核心生命周期方法(init()service()destroy()等),是 Servlet 规范的 "顶层契约"。
  2. GenericServlet 抽象类 :实现了 Servlet 接口中service()外的所有方法 (帮我们简化了配置获取、信息获取等通用逻辑),但service()仍需子类实现。
  3. HttpServlet 抽象类 :继承 GenericServlet,实现了service()方法 ,并把它拆分成对应 HTTP 请求方式的方法(doGet()doPost()doPut()等)------ 这样我们不用自己判断请求类型,只需重写对应方法即可。
  4. 自定义类 :继承 HttpServlet,重写doGet()/doPost()等方法,完成具体的业务逻辑
层级 作用 & 核心点
Servlet 接口 定义生命周期核心方法(init()/service()/destroy()等),是规范顶层契约
GenericServlet 实现 Servlet 接口的 "非 service 方法",简化配置 / 信息获取的通用逻辑
HttpServlet 继承 GenericServlet,实现service()并拆分出doGet()/doPost()适配 HTTP 请求
自定义类 继承 HttpServlet,重写doGet()/doPost()写业务逻辑

2、Servlet 的加载(生命周期)原理

Servlet 的加载 是由Web 容器(如 Tomcat) 管理的,核心流程是:

  1. 实例化(创建对象) Web 容器在第一次收到该 Servlet 的请求时(或容器启动时,若配置了load-on-startup),通过反射创建 Servlet 对象。

  2. 初始化(init() 容器调用init(ServletConfig config)方法,完成 Servlet 的初始化(比如加载配置、初始化资源)。注:GenericServlet 已经帮我们实现了init()的通用逻辑,我们一般不用重写,除非有自定义初始化需求。

  3. 提供服务(service() 每次收到请求,容器都会调用service()方法:

    • 若直接实现 Servlet 接口:需要自己在service()里处理请求 / 响应。
    • 若继承 HttpServlet:service()会自动根据 HTTP 请求方式(GET/POST 等),调用对应的doGet()/doPost()(这也是我们实际开发中重写这两个方法的原因)。
  4. 销毁(destroy() 当 Web 容器关闭或 Servlet 被移除时,调用destroy()方法,释放资源(比如关闭连接、清理缓存)。

3、为什么要这么设计?

这套结构是为了 **简化开发**:

  • Servlet 接口定义规范,但直接实现太繁琐(要写所有方法)。
  • GenericServlet 帮我们做了通用逻辑的 "兜底实现"。
  • HttpServlet 进一步适配 HTTP 协议,把service()拆分成 HTTP 请求对应的方法,让我们只需要关注具体请求的业务逻辑。

2、tomcat执行流程

上图为Tomcat 执行流程,将其拆成更细以下几个步骤

步骤 1:请求到达服务器

用户通过浏览器发送http://ip:8080/xxx/my请求 → 先到达服务器的网卡(Tomcat 已在服务器上注册了 8080 端口,所以请求会被 Tomcat 接收)。

步骤 2:Tomcat 通过 Socket 建立连接

Tomcat 通过Socket(网络通信组件) 打开输入流(InputStream) ,接收请求数据;同时准备好输出流(OutputStream),用于后续返回响应。

步骤 3:解析 HTTP 请求数据

Tomcat 通过输入流读取请求内容,并解析请求的核心信息,包括:

  • 来源 IP、端口
  • URL 地址(比如这里的/my
  • 请求类型(比如这里的get
  • Cookie、请求参数等数据

步骤 4:Servlet 容器匹配对应的 Servlet 对象

Tomcat 内部有一个Servlet 容器(本质是 "地址→Servlet 对象的地址" 的映射表):

  • 容器根据解析出的请求地址(比如/my),找到对应的MyServlet对象(图中地址是 0x2);
  • 若请求地址是/login,则会匹配loginServlet对象(地址 0x1)。

步骤 5:调用 Servlet 的业务方法处理请求

找到对应的MyServlet对象后,Tomcat 会根据请求类型(这里是get),调用 Servlet 中对应的方法(比如doGet()):

  • doGet()方法中,会执行具体的业务逻辑(比如处理请求参数、操作数据库等),最终生成响应数据(response)

步骤 6:返回响应

Tomcat 通过输出流(OutputStream) ,把doGet()生成的响应数据,通过 Socket、网卡返回给浏览器,完成一次请求的处理。

简单总结:请求到网卡→Socket 接流→解析请求→容器匹配 Servlet→调用 do 方法处理→返回响应

有三个是 Tomcat 等服务器常用的网络 IO 模型,核心是处理 "请求连接 + 数据读写" 的方式不同,我用通俗的方式解释:

1. BIO(Blocking IO,阻塞 IO)

  • 本质:"一个请求对应一个线程",全程阻塞。
  • 过程 :当客户端发起请求,Tomcat 会启动一个新线程 去处理这个请求:
    • 线程会一直 "堵" 在Socket的输入流 / 输出流上(比如等请求数据、等响应写完);
    • 直到这个请求完全处理完,线程才会被释放。
  • 特点:简单易理解,但线程资源消耗大(高并发时线程数爆炸)。

2. NIO(Non-Blocking IO,非阻塞 IO)

  • 本质 :"一个线程管理多个请求",基于多路复用器(Selector) 实现。
  • 过程 :Tomcat 用一个(或少量)线程通过Selector监听所有连接的状态
    • 当某个连接的 "请求数据到了""可以写响应了",Selector会通知线程处理这个连接;
    • 线程处理时不会阻塞(没数据就去处理其他连接)。
  • 特点:线程利用率高,能支撑更高并发(Tomcat 默认用 NIO)。

3. AIO(Asynchronous IO,异步 IO)

  • 本质:"请求提交后不用管,等系统通知结果",完全异步。
  • 过程:Tomcat 发起 "读 / 写请求" 后,直接去做其他事;等操作系统把数据准备好(或写完成),会主动通知 Tomcat 来处理结果。
  • 特点:彻底解放线程,但实现复杂,适合连接数极多但数据交互少的场景(Tomcat 用得较少)。

对比总结

模型 核心逻辑 并发能力 复杂度
BIO 一请求一线程,全程阻塞 简单
NIO 线程 + Selector 管多连接 中高 中等
AIO 异步通知,线程不等待 复杂

3、手写tomcat思路流程

上图展示了 **手写简易 Tomcat" 的核心实现流程 **,本质是模拟 Tomcat 的 "请求处理 + Servlet 映射" 逻辑,分启动阶段请求处理阶段详细拆解:

3.1 启动阶段:初始化 Servlet 容器

手写 Tomcat 启动时,要先准备好 "Servlet 映射表",步骤如下:

  1. 扫描项目文件 遍历项目目录,找到所有标记了@WebServlet注解的类,获取这些类的全路径名 (比如com.example.MyServlet)。

  2. 解析 Servlet 配置信息 读取@WebServlet注解中的访问路径 (比如/my),这是客户端访问该 Servlet 的 URL 路径。

  3. 生成 Servlet 容器(本质是 HashMap) 创建一个HashMap作为 "Servlet 映射表":

    • Key :Servlet 的访问路径(比如/my);
    • Value :通过反射 创建的 Servlet 对象(用类的全路径名,调用Class.forName(类名).newInstance()生成实例)。

3.2 请求处理阶段:接收请求→处理→响应

当客户端发送请求后,手写 Tomcat 会按以下流程处理:

  1. 接收请求(Socket 通信) 通过Socket打开输入流 ,读取客户端发送的 HTTP 请求信息(比如请求行、请求头、请求体);同时打开输出流,用于后续返回响应。

  2. 封装请求对象(HttpServletRequest) 创建自定义的HttpServletRequest类,把读取到的请求信息(路径、方法、参数等)全部封装到这个对象中,方便后续使用。

  3. 匹配请求目标HttpServletRequest中获取请求路径,用这个路径作为 Key,去查之前创建的HashMap容器:

    • 如果匹配到 Servlet :从容器中取出对应的 Servlet 对象,再根据请求方法(GET/POST),调用 Servlet 的doGet()/doPost()方法处理请求。
    • 如果没匹配到 Servlet :判断请求路径是否对应静态资源 (比如.html、.css、.js):
      • 是静态资源:读取资源文件,准备返回;
      • 不是:返回 404 错误。
  4. 封装响应对象(HttpServletResponse) 创建自定义的HttpServletResponse类,把处理结果(Servlet 的业务数据 / 静态资源内容 / 404 信息)封装到这个对象中。

  5. 返回响应 通过Socket的输出流,把HttpServletResponse中的数据以 HTTP 格式写回客户端,完成请求处理。

3.3 核心本质

手写 Tomcat 的核心是 **"反射 + HashMap 映射 + Socket 通信"**:用反射创建 Servlet 对象,用 HashMap 管理 "路径→Servlet" 的映射,用 Socket 完成网络请求的收发,模拟了 Tomcat 的核心工作逻辑。

4、具体手写tomcat

4.1 首先在idea中创建maven项目

4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。

4.3 在webapps里面创建MyfirstServlet和MysecondServlet。

4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承​​​​​GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。

4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。

4.6 这是我们考虑到了第三步@WebServlet注解。

关于注解我们需要了解最重要的三点: 注解作用范围、注解的生命周期以及注解名字的规定

java 复制代码
@Retention(RetentionPolicy.RUNTIME)     //自定义注解的生命周期
@Target(ElementType.TYPE)     //自定义注解的作用范围,就是可以放在哪里
public @interface WebServlet {
    String value(); //默认的     类型    名称()
}

其中String 默认为value(),但是如果变为其他的命名:需要在使用时进行具体赋值。

例如:

java 复制代码
@Retention(RetentionPolicy.RUNTIME)     //自定义注解的生命周期
@Target(ElementType.TYPE)     //自定义注解的作用范围,就是可以放在哪里
public @interface WebServlet {
    String val(); //默认的     类型    名称()
}
java 复制代码
@WebServlet(val = "/myfirst")
public class MyfirstServlet extends HttpServlet {

}

4.7接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。

4.7.1反射获取全类名的三种方式:

1.通过 Class.forName() 获取(动态加载类)

2.没有对象实例的场景,直接通过 类名.class 获取 Class 对象

3.通过对象的 getClass() 方法获取 Class 对象

要一一扫描项目文件 遍历项目目录,找到所有标记了@WebServlet注解的类,获取这些类的全路径名 (比如com.example.MyServlet)我们可以定义一个工具类

4.7.2关于工具类:

1.都是静态方法,要对外访问的用public,不让外界访问的私有化private。

2.有main方法,不创建对象

工具包:

java 复制代码
import java.io.File;
import java.util.ArrayList;
import java.util.List;

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

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

创建servlet容器利用hashmap

ServletConfigMapping:生成servlet容器

java 复制代码
import com.qcby.lib.HttpServlet;
import com.qcby.lib.WebServlet;
import com.qcby.utils.SearchClassUtil;


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

/**
 * servlet容器配置类
 */
public class ServletConfigMapping {
    /**
     *
     */
    public static Map<String, HttpServlet> classMapping = new HashMap<>();//servlet容器

    //特点:在main方法执行之前加载,以修饰main方法
    static {
            // 1. 扫描指定包下的所有类
            List<String> paths = SearchClassUtil.searchClass("com.qcby.webapps.myweb");
        try {
            // 2. 遍历类,解析@WebServlet注解
            for (String path : paths) {
                //获取类的注解信息,并将path作为key值,将对象作为value值 存放在classMapping当中
                Class clazz = Class.forName(path);//获取类对象
                //获取类的注解信息
// 通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象,后续可通过该对象获取注解的属性(比如value()获取 Servlet路径、name()获取 Servlet名称等)。
                WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
                //System.out.println(webServlet.value());
                //这里获取类的无参构造方法 ---创建新的对象并强转向上转型为HttpServlet
                HttpServlet servlet = (HttpServlet)clazz.getDeclaredConstructor().newInstance();
                classMapping.put(webServlet.value(), servlet);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.7.3需要注意的是:

1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型)

2.扫描包的类用的是一个SearchClassUtil工具类进行实现的

2.1 首先 进行遍历获取类信息 Class clazz = Class.forName(path)

2.2通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象

2.3这里获取类的无参构造方法 ---创建新的对象并强转向上转型为

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

2.4 存入servlet容器 classMapping.put(webServlet.value(), servlet);

4.8 新MyTomcat文件,在MyTomcat启动器里面获取请求信息,我们会发现在浏览器里面访问一个资源的时候,idea会返回请求信息,因为我们定义了一个端口号7788

MyTom启动类:

java 复制代码
import com.qcby.config.ServletConfigMapping;
import com.qcby.lib.HttpServlet;
import com.qcby.lib.HttpServletRequest;
import com.qcby.lib.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyTomcat {

    //创建HttpServletRequest的对象用来保存请求信息的
    private static HttpServletRequest httpServletRequest = new HttpServletRequest();
    //创建HttpServletResponse的对象用来返回给客户端前端
    private static HttpServletResponse httpServletResponse = new HttpServletResponse();

    private static final Integer PORT = 7788;   //监听的端口号

    public static void start() throws Exception {
        ServerSocket serverSocket = new ServerSocket(PORT);
        Socket socket = serverSocket.accept();//阻塞监听
        InputStream stream = socket.getInputStream();//输入流
        httpServletResponse.setOutputStream(socket.getOutputStream());//输出流

        hanlder(stream);
    }
    /**
     * 处理客户端的请求数据,并进行保存
     * @param stream
     * @throws IOException
     */
    public static void hanlder(InputStream stream) throws Exception {
        int count = 0;
        while(count == 0){
            count = stream.available();
        }
        byte[] bytes = new byte[count];
        int read = stream.read(bytes);
        String request = new String(bytes, 0, read);
        //System.out.println(request);
        if(request.equals("")){
            System.out.println("请求为null");
        }else{
            String firstLine = request.split("\n")[0];
            String method = firstLine.split("\\s")[0];//请求方式
            String path = firstLine.split("\\s")[1];//请求路径
            System.out.println("请求方式:"+method+' '+"请求路径:"+path);

            httpServletRequest.setMethod(method);//传入
            httpServletRequest.setPath(path);//传入

            if (path.equals("")){
                System.out.println("请求为空");
            }else if (ServletConfigMapping.classMapping.get(path) != null){
                //动态
                //虽然是用的HttpServlet对象进行接收,但是实际上我们返回的是MyFirstServlet/MySecondServlet
                //HttpServlet 实际上是向上转型的产物
                HttpServlet servlet = ServletConfigMapping.classMapping.get(path);
                servlet.service(httpServletRequest,httpServletResponse);
            }else{
                //静态
                httpServletResponse.returnStatic(path);
            }
        }
    }
    /**
     * tomcat启动类
     */
    public static void main(String[] args) throws Exception {
        start();
    }
}

4.9 在hettpservletrequest中实现servletrequest的get和set方法,用来保存4.8 获取请求信息,比如method请求方法,path请求路径。

ServletRequest接口:

java 复制代码
public interface ServletRequest {
    //定义get和set
    String getMethod();
    String getPath();
    String setMethod(String method);
    String setPath(String path);
}

ServletResponse接口:

java 复制代码
public interface ServletResponse {
    void append(String responseHeader200) throws Exception;
}

HttpServletRequest类:

java 复制代码
public class HttpServletRequest implements ServletRequest{
    private String method;
    private String path;


    @Override
    public String getMethod() {
        return method;
    }

    @Override
    public String getPath() {
        return path;
    }

    @Override
    public String setMethod(String method) {
        return this.method = method;
    }

    @Override
    public String setPath(String path) {
        return this.path = path;
    }
}

HttpServletResponse类:

java 复制代码
package com.qcby.lib;

import com.qcby.utils.FileUtil;

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

public class HttpServletResponse implements ServletResponse{
    //获取输出流
    private OutputStream outputStream;

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

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

    /**
     * 返回静态资源
     * @param path      静态资源文件的相对路径
     * @throws Exception
     */
    public void returnStatic(String path) throws Exception{
        String resouce = FileUtil.getResoucePath(path);//获取静态资源的真实路径
        File file = new File(resouce);  //静态文件
        if (file.exists()){
            //静态文件存在
            FileUtil.writeFile(file,outputStream);
        }else{
            System.out.println("404静态资源文本不存在......");
        }
    }
}

在输入流返回信息中需要进行信息的截取

4.10 此时request里面已经有数据了,此时回看之前跳过的4.5编写dopost和get方法的时候,编写请求里面具体的post和get方法。

Servlet接口:

java 复制代码
public interface Servlet {

    void init() ;

    void service(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception;

    void destroy();
}

GenericServlet抽象类:实现了Servlet接口

java 复制代码
public abstract class GenericServlet implements Servlet{
    @Override
    public void init() {

    }

    @Override
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception;

    @Override
    public void destroy() {

    }
}

HttpServlet抽象类:继承了GenericServlet为父类

java 复制代码
package com.qcby.lib;

public abstract class HttpServlet extends GenericServlet {
    public abstract void doGet(ServletRequest request, ServletResponse response) throws Exception;

    public abstract void doPost(ServletRequest request, ServletResponse response);

    @Override
    public void service(ServletRequest request, ServletResponse response) throws Exception {
        //此时已经有servletRequest的信息
        //对应的请求
        if (request.getMethod().equals("GET")) {
            doGet(request, response);
        } else if (request.getMethod().equals("Post")) {
            doPost(request, response);
        }
    }
}

例如:自定义类MyfirstServlet

java 复制代码
package com.qcby.webapps.myweb;

import com.qcby.lib.HttpServlet;
import com.qcby.lib.ServletRequest;
import com.qcby.lib.ServletResponse;
import com.qcby.lib.WebServlet;
import com.qcby.utils.ResponseUtil;

/**
 * 我们在创建第一个servlet程序的时候注意了三点
 * 1.继承HttpServlet
 * 2.重写doget或者dopost方法
 * 3.引入@WebServlet注解 --- 详细注解的注意事项
 */
@WebServlet("/myfirst")
public class MyfirstServlet extends HttpServlet {

    @Override
    public void doGet(ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("MyFirstServlet doGet 这里的地址");
        response.append(ResponseUtil.getResponseHeader200("<h1>这里是第一个Servlet</h1>"));
    }

    @Override
    public void doPost(ServletRequest request, ServletResponse response) {

    }
}

MysecondServlet:

java 复制代码
package com.qcby.webapps.myweb;

import com.qcby.lib.HttpServlet;
import com.qcby.lib.ServletRequest;
import com.qcby.lib.ServletResponse;
import com.qcby.lib.WebServlet;

@WebServlet("/mysecond")
public class MysecondServlet extends HttpServlet {
    @Override
    public void doGet(ServletRequest request, ServletResponse response) {

    }

    @Override
    public void doPost(ServletRequest request, ServletResponse response) {

    }
}

4.11 将请求信息与map集合的servlet进行匹配进行执行,

MyTomcat中:

java 复制代码
if (path.equals("")){
                System.out.println("请求为空");
            }else if (ServletConfigMapping.classMapping.get(path) != null){
                //动态
                //虽然是用的HttpServlet对象进行接收,但是实际上我们返回的是MyFirstServlet/MySecondServlet
                //HttpServlet 实际上是向上转型的产物
                HttpServlet servlet = ServletConfigMapping.classMapping.get(path);
                servlet.service(httpServletRequest,httpServletResponse);
            }else{
                //静态
                httpServletResponse.returnStatic(path);
            }

4.12 返回

静态资源:引入一个文件工具类

java 复制代码
package com.qcby.utils;

import java.io.*;

/**
 * 该类的主要作用是进行读取文件
 */
public class FileUtil {

    public  static  boolean witeFile(InputStream inputStream, OutputStream outputStream){
        boolean success = false ;
        BufferedInputStream bufferedInputStream ;
        BufferedOutputStream bufferedOutputStream;

        try {
            bufferedInputStream = new BufferedInputStream(inputStream);
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            bufferedOutputStream.write(ResponseUtil.responseHeader200.getBytes());

            int count = 0;
            while (count == 0){
                count = inputStream.available();
            }
            int fileSize = inputStream.available();
            long written = 0;
            int beteSize = 1024;
            byte[] bytes = new byte[beteSize];
            while (written < fileSize){
                if(written + beteSize > fileSize){
                    beteSize = (int)(fileSize - written);
                    bytes = new byte[beteSize];
                }
                bufferedInputStream.read(bytes);
                bufferedOutputStream.write(bytes);
                bufferedOutputStream.flush();
                written += beteSize;
            }
            success = true;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return success;
    }

    public static boolean writeFile(File file,OutputStream outputStream) throws Exception{
        return witeFile(new FileInputStream(file),outputStream);
    }


    public static String getResoucePath(String path){
        String resource = FileUtil.class.getResource("/").getPath();
        return resource + "\\" + path;
    }

}

在tomcat启动类中

java 复制代码
else{
                //静态
                httpServletResponse.returnStatic(path);
            }

HttpServletResponse响应类

java 复制代码
    /**
     * 返回静态资源
     * @param path      静态资源文件的相对路径
     * @throws Exception
     */
    public void returnStatic(String path) throws Exception{
        String resouce = FileUtil.getResoucePath(path);//获取静态资源的真实路径
        File file = new File(resouce);  //静态文件
        if (file.exists()){
            //静态文件存在
            FileUtil.writeFile(file,outputStream);
        }else{
            System.out.println("404静态资源文本不存在......");
        }
    }
相关推荐
筱顾大牛2 小时前
Redission快速入门---分布式锁
java·redis·分布式·缓存
自在极意功。2 小时前
ArrayList扩容机制
java·开发语言·算法·集合·arraylist
Volunteer Technology2 小时前
核心框架源码常见问题(上)
java·后端·spring
吃鱼不吐刺.2 小时前
Java线程池
java·开发语言
小沛92 小时前
从“会敲 Arthas 命令”到“会做线上诊断”:我做了一个 Arthas Agent 工具
java·jvm·spring·agent
短剑重铸之日2 小时前
《ShardingSphere解读》04 配置驱动:ShardingSphere 中的配置体系是如何设计的?
java·数据库·后端·spring·shardingsphere
独断万古他化2 小时前
【抽奖系统开发实战】Spring Boot 项目的设计思路、技术选型与公共模块处理
java·spring boot·后端·系统架构
健康平安的活着2 小时前
java8案例对list[过滤、分组,转换,查找等]清洗逻辑
java·数据结构·list
花间相见2 小时前
【JAVA基础09】—— 赋值与三元运算符:从基础到实操的避坑指南
java·开发语言·python