【Tomcat】手写迷你版Tomcat

源码地址

https://github.com/CoderXiaohui/mini-tomcat

一,分析

Mini版Tomcat需要实现的功能

作为一个服务器软件提供服务(通过浏览器客户端发送Http请求,它可以接收到请求进行处理,处理之后的结果返回浏览器客户端)。

  1. 提供服务,接收请求(socket通信)
  2. 请求信息封装成Request对象,封装响应信息Response对象
  3. 客户端请求资源,资源分为静态资源(html)和动态资源(servlet)
  4. 资源返回给客户端浏览器

*Tomcat的入口就是一个main函数

二,开发——准备工作

2.1 新建Maven工程

image-20201227160427940

image-20201227161023307

2.2 定义编译级别

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dxh</groupId>
    <artifactId>MiniCat</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.3 新建主类编写启动入口和端口

这里我们把socket监听的端口号定义在主类中。

package server;

/**
 * Minicat的主类
 */
public class Bootstrap {
    /**
     * 定义Socket监听的端口号
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat的启动入口
     * @param args
     */
    public static void main(String[] args) {
        
    }
}

三,开发——1.0版本

循序渐进,一点一点的完善,1.0版本我们需要的需求是:

  • 浏览器请求http://localhost:8080,返回一个固定的字符串到页面“Hello Minicat”

3.1 编写start方法以及遇到的问题

start方法主要就是监听上面配置的端口,然后得到其输出流,最后写出。

/**
 * MiniCat启动需要初始化展开的一些操作
 */
public void start() throws IOException {
    /*
       完成Minicat 1.0版本
       需求:浏览器请求http://localhost:8080,返回一个固定的字符串到页面“Hello Minicat!”
     */
    ServerSocket serverSocket = new ServerSocket(port);
    System.out.println("========>>Minicat start on port:"+port);

    while(true){
        Socket socket = serverSocket.accept();
        //有了socket,接收到请求,获取输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("Hello Minicat!".getBytes());
        socket.close();
    } 
}

完整的代码:


/**
 * Minicat的主类
 */
public class Bootstrap {
    /**
     * 定义Socket监听的端口号
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat的启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            //启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("========>>Minicat start on port:"+port);

        while(true){
            Socket socket = serverSocket.accept();
            //有了socket,接收到请求,获取输出流
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("Hello Minicat!".getBytes());
            socket.close();
        }
    }

}

此时,如果启动项目,从浏览器中输入http://localhost:8080/,能够正常接收到请求吗?

不能!

问题分析:

启动项目,从浏览器中输入http://localhost:8080/,可看到返回结果如下图:
image-20201227165334660

因为Http协议是一个应用层协议,其规定了请求头、请求体、响应同样,如果没有这些东西的话浏览器无法正常显示。代码中直接把”Hello Minicat!“直接输出了,

3.2 解决问题,修改代码:

  1. 新建一个工具类,主要提供响应头信息

    package server;
    
    /**
     * http协议工具类,主要提供响应头信息,这里我们只提供200和404的情况
     */
    public class HttpProtocolUtil {
    
        /**
         *  为响应码200提供请求头信息
         */
        public static String getHttpHeader200(long contentLength){
            return "HTTP/1.1 200 OK \n" +
                    "Content-Type: text/html \n" +
                    "Content-Length: "+contentLength +"\n"+
                    "\r\n";
        }
    
    
        /**
         *  为响应码404提供请求头信息(也包含了数据内容)
         */
        public static String getHttpHeader404(){
            String str404="<h1>404 not found</h1>";
            return "HTTP/1.1 404 NOT Found \n" +
                    "Content-Type: text/html \n" +
                    "Content-Length: "+str404.getBytes().length +"\n"+
                    "\r\n" + str404;
        }
    
    }
    
    
  2. 修改start方法

        public void start() throws IOException {
    
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("========>>Minicat start on port:"+port);
    
            while(true){
                Socket socket = serverSocket.accept();
               
                OutputStream outputStream = socket.getOutputStream();
                String data = "Hello Minicat!";
                String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
                outputStream.write(responseText.getBytes());
                socket.close();
            }
        }
    
  3. 访问~
    image-20201227172649749

成功。

四,开发——2.0版本

需求:

  • 封装Request和Response对象
  • 返回html静态资源文件

4.1 封装前准备

新建一个类,Bootstrap2 (为了方便与1.0版本做对比)。获得输入流,并打印出来看看。

package server;

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

/**
 * Minicat的主类
 */
public class Bootstrap2 {
    /**
     * 定义Socket监听的端口号
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat的启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap2 bootstrap = new Bootstrap2();
        try {
            //启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("========>>Minicat start on port:"+port);

        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);
            System.out.println("请求信息=====>>"+new String(bytes));
            socket.close();
        }

    }

}

打印出来的信息:

image-20201227191058136

这里我们需要得到的是 请求方式(GET) 和 url (/) ,接下来封装Request的时候也是只封装这两个属性

4.2封装Request、Response对象

4.2.1 封装Request

只封装两个参数——method和url

  1. 新建Request类

  2. 该类有三个属性(String methodString urlInputStream inputStream
    method和url都是从input流中解析出来的。

  3. GET SET方法

  4. 编写有参构造

    /**
     * 构造器 输入流传入
     */
    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;
        //从输入流中获取请求信息
        int count = 0 ;
        while (count==0){
            count = inputStream.available();
        }
        byte[] bytes = new byte[count];
        inputStream.read(bytes);
        String inputsStr = new String(bytes);
        //获取第一行数据
        String firstLineStr = inputsStr.split("\\n")[0];  //GET / HTTP/1.1
        String[] strings = firstLineStr.split(" ");
        //把解析出来的数据赋值
        this.method=strings[0];
        this.url= strings[1];
    
        System.out.println("method=====>>"+method);
        System.out.println("url=====>>"+url);
    }
    
  5. 无参构造

完整的Request.java

package server;

import java.io.IOException;
import java.io.InputStream;

/**
 * 把我们用到的请求信息,封装成Response对象 (根据inputSteam输入流封装)
 */
public class Request {
    /**
     * 请求方式 例如:GET/POST
     */
    private String method;

    /**
     * / , /index.html
     */
    private String url;

    /**
     * 其他的属性都是通过inputStream解析出来的。
     */
    private InputStream inputStream;

    /**
     * 构造器 输入流传入
     */
    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;
        //从输入流中获取请求信息
        int count = 0 ;
        while (count==0){
            count = inputStream.available();
        }
        byte[] bytes = new byte[count];
        inputStream.read(bytes);
        String inputsStr = new String(bytes);
        //获取第一行数据
        String firstLineStr = inputsStr.split("\\n")[0];  //GET / HTTP/1.1
        String[] strings = firstLineStr.split(" ");
        this.method=strings[0];
        this.url= strings[1];

        System.out.println("method=====>>"+method);
        System.out.println("url=====>>"+url);
    }

    public Request() {
    }

    public String getMethod() {
        return method;
    }

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

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
a
    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }
}

4.2.2 封装Response

package server;

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

/**
 * 封装Response对象,需要依赖于OutputStream
 *
 */
public class Response{
    private OutputStream outputStream;

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

    public Response() {
    }

    /**
     * @param path 指的就是 Request中的url ,随后要根据url来获取到静态资源的绝对路径,进一步根据绝对路径读取该静态资源文件,最终通过输出流输出
     */
    public void outputHtml(String path) throws IOException {
        //获取静态资源的绝对路径
        String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);

        //输出静态资源文件
        File file = new File(absoluteResourcePath);
        if (file.exists() && file.isFile()){
            //读取静态资源文件,输出静态资源
            StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
        }else{
            //输出404
            output(HttpProtocolUtil.getHttpHeader404());
        }
    }

    //使用输出流输出指定字符串
    public void output(String context) throws IOException {
        outputStream.write(context.getBytes());
    }
}

2.0版本只考虑输出静态资源文件

我们来分析一下outputHtml(String path)这个方法

首先,path就指 Request中的url,我们要用这个url找到该资源的绝对路径:

  1. 根据path,获取静态资源的绝对路径

    public static String getAbsolutePath(String path){
        String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
        return absolutePath.replaceAll("\\\\","/")+path;
    }
    
  2. 判断静态资源是否存在

    • 不存在:输出404
  3. 存在:读取静态资源文件,输出静态资源

    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
            int count = 0 ;
            while (count==0){
                count=inputStream.available();
            }
            //静态资源长度
            int resourceSize = count;
            //输出Http请求头 , 然后再输出具体的内容
            outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
    
            //读取内容输出
            long written = 0;   //已经读取的内容长度
            int byteSize = 1024; //计划每次缓冲的长度
            byte[] bytes = new byte[byteSize];
    
            while (written<resourceSize){
                if (written+byteSize >resourceSize){    //剩余未读取大小不足一个1024长度,那就按照真实长度处理
                    byteSize= (int)(resourceSize-written);  //剩余的文件内容长度
                    bytes=new byte[byteSize];
                }
                inputStream.read(bytes);
                outputStream.write(bytes);
                outputStream.flush();
    
                written+=byteSize;
            }
        }
    

把上述的第一步和第三步的方法封装到一个类中:

package server;

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

public class StaticResourceUtil {

    /**
     * 获取静态资源方法的绝对路径
     */
    public static String getAbsolutePath(String path){
        String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
        return absolutePath.replaceAll("\\\\","/")+path;
    }


    /**
     * 读取静态资源文件输入流,通过输出流输出
     */
    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
        int count = 0 ;
        while (count==0){
            count=inputStream.available();
        }
        //静态资源长度
        int resourceSize = count;
        //输出Http请求头 , 然后再输出具体的内容
        outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());

        //读取内容输出
        long written = 0;   //已经读取的内容长度
        int byteSize = 1024; //计划每次缓冲的长度
        byte[] bytes = new byte[byteSize];

        while (written<resourceSize){
            if (written+byteSize >resourceSize){    //剩余未读取大小不足一个1024长度,那就按照真实长度处理
                byteSize= (int)(resourceSize-written);  //剩余的文件内容长度
                bytes=new byte[byteSize];
            }
            inputStream.read(bytes);
            outputStream.write(bytes);
            outputStream.flush();

            written+=byteSize;
        }
    }

}

测试:

  1. 修改Bootstrap2.java中的start()方法

    public void start() throws IOException {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("========>>Minicat start on port:"+port);
    
            while (true){
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                //封装Resuest对象和Response对象
                Request request = new Request(inputStream);
                Response response = new Response(socket.getOutputStream());
                response.outputHtml(request.getUrl());
                socket.close();
           }
    }
    
  2. 在项目的resources文件夹新建index.html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Static resource </title>
    </head>
    <body>
    Hello ~ Static resource
    </body>
    </html>
    
  3. 运行main方法

  4. 浏览器输入:http://localhost:8080/index.html

  5. 结果展现:
    image-20201227205755188

五,开发——3.0版本

3.0版本就要定义Servlet了,大致分为以下几步:

  1. 定义servlet规范
  2. 编写Servlet
  3. 加载解析Servlet配置

5.1 定义servlet规范

public interface Servlet {
    void init() throws Exception;
    void destroy() throws Exception;
    void service(Request request,Response response) throws Exception;
}

定义一个抽象类,实现Servlet,并且增加两个抽象方法doGet , doPost.

public abstract class HttpServlet implements Servlet{

    public abstract void doGet(Request request,Response response);
    public abstract void doPost(Request request,Response response);


    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void service(Request request, Response response) throws Exception {
        if ("GET".equals(request.getMethod())){
            doGet(request, response);
        }else{
            doPost(request, response);
        }
    }
}

5.2 编写Servlet继承HttpServlet

新建DxhServlet.java,并继承HttpServlet重写doGet和doPost方法

package server;

import java.io.IOException;

public class DxhServlet extends HttpServlet{
    @Override
    public void doGet(Request request, Response response) {
        String content="<h1>DxhServlet get</h1>";
        try {
            response.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length)+content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(Request request, Response response) {
        String content="<h1>DxhServlet post</h1>";
        try {
            response.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length)+content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init() throws Exception {
        super.init();
    }

    @Override
    public void destroy() throws Exception {
        super.destroy();
    }
}

接下来要把DxhServlet配置到一个配置文件中,当MiniCat启动时,加载进去。

5.3 加载解析Servlet配置

5.3.1 配置文件

resources目录下,新建web.xml

<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
    <servlet>
        <servlet-name>dxh</servlet-name>
        <servlet-class>server.DxhServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>dxh</servlet-name>
        <url-pattern>/dxh</url-pattern>
    </servlet-mapping>
</web-app>

标准的配置Servlet的标签。servlet-class改成自己写的Servlet全限定类名,url-pattern/dxh,一会请求http://localhost:8080/dxh,来访问这个servlet

5.3.2 解析配置文件

复制一份Bootstrap2.java,命名为Bootstrap3.java

  1. 加载解析相关的配置 ,web.xml
    引入dom4j和jaxen的jar包

    <dependency>
    	<groupId>dom4j</groupId>
    	<artifactId>dom4j</artifactId>
    	<version>1.6.1</version>
    </dependency>
    <dependency>
    	<groupId>jaxen</groupId>
    	<artifactId>jaxen</artifactId>
    	<version>1.1.6</version>
    </dependency>
    
  2. Bootstrap3.java中增加一个方法

    //用于下面存储url-pattern以及其对应的servlet-class的实例化对象
    private Map<String,HttpServlet> servletMap = new HashMap<>();
    
    private void loadServlet(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //根元素
            Element rootElement = document.getRootElement();
            /**
             * 1, 找到所有的servlet标签,找到servlet-name和servlet-class
             * 2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
             */
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                /**
                 * 1, 找到所有的servlet标签,找到servlet-name和servlet-class
                 */
                //<servlet-name>dxh</servlet-name>
                Element servletNameElement =(Element)element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                //<servlet-class>server.DxhServlet</servlet-class>
                Element servletClassElement =(Element)element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();
                /**
                 * 2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
                 */
                //Xpath表达式:从/web-app/servlet-mapping下查询,查询出servlet-name=servletName的元素
                Element servletMapping =(Element)rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']'");
                // /dxh
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern,(HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    

    这段代码的意思就是读取web.xml转换成Document,然后遍历根元素内中的servlet标签(servlet是可以配置多个的),通过XPath表达式获得servlet-nameservlet-class,以及与其对应的<servlet-mapping>标签下的url-pattern,然后存在Map中。注意,这里Map的Key是url-patternValue是servlet-class的实例化对象

5.4 接收请求,处理请求改造

image-20201227230820862

image-20201227231617987

这里进行了判断,判断servletMap中是否存在url所对应的value,如果没有,当作静态资源访问,如果有,取出并调用service方法,在HttpServlet的service方法中已经做了根据request判断具体调用的是doGet还是doPost方法。

测试:

在浏览器中输入:
http://localhost:8080/index.html,可以访问静态资源
image-20201227231858786

输入:http://localhost:8080/dxh
image-20201227231925246

可以访问【5.2中编写的Servlet】动态资源~

到此位置,一个简单的Tomcat Demo已经完成。

六,优化——多线程改造(不使用线程池)

6.1 问题分析

在现有的代码中,接收请求这部分它是一个IO模型——BIO,阻塞IO。

它存在一个问题,当一个请求还未处理完成时,再次访问,会出现阻塞的情况。

可以在DxhServletdoGet方法中加入Thread.sleep(10000);然后访问http://localhost:8080/dxhhttp://localhost:8080/index.html做个测试

那么我们可以使用多线程对其进行改造。
image-20201227233057004

把上述代码放到一个新的线程中处理。

6.2 复制Bootstrap3

复制Bootstrap3,命名为Bootstrap4。把start()方法中上图的部分(包括socket.close()剪切到下面的线程处理类的run方法中:

6.3 定义一个线程处理类

package server;

import java.io.InputStream;
import java.net.Socket;
import java.util.Map;

/**
 * 线程处理类
 */
public class RequestProcessor extends Thread{
    private  Socket socket;
    private Map<String,HttpServlet> servletMap;

    public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
        this.socket = socket;
        this.servletMap = servletMap;
    }

    @Override
    public void run() {
        try{
            InputStream inputStream = socket.getInputStream();
            //封装Resuest对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());
            String url = request.getUrl();
            //静态资源处理
            if (servletMap.get(url)==null){
                response.outputHtml(request.getUrl());
            }else{
                //动态资源处理
                HttpServlet httpServlet = servletMap.get(url);
                httpServlet.service(request,response);
            }
            socket.close();
        }catch (Exception e){

        }
    }
}

6.4 修改Bootstrap4的start()方法

public void start() throws Exception {
    //加载解析相关的配置 ,web.xml,把配置的servlet存入servletMap中
    loadServlet();

    ServerSocket serverSocket = new ServerSocket(port);
    System.out.println("========>>Minicat start on port:"+port);
    /**
     * 可以请求动态资源
     */
    while (true){
        Socket socket = serverSocket.accept();
        //使用多线程处理
        RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
        requestProcessor.start();
    }
}

再次做6.1章节的测试, OK 没有问题了。

七,优化——多线程改造(使用线程池)

这一步,我们使用线程池进行改造。

复制Bootstrap4,命名为Bootstrap5。

修改start()方法。线程池的使用不再赘述。代码如下:

public void start() throws Exception {
    //加载解析相关的配置 ,web.xml,把配置的servlet存入servletMap中
    loadServlet();

    /**
     * 定义线程池
     */
    //基本大小
    int corePoolSize = 10;
    //最大
    int maxPoolSize = 50;
    //如果线程空闲的话,超过多久进行销毁
    long keepAliveTime = 100L;
    //上面keepAliveTime的单位
    TimeUnit unit = TimeUnit.SECONDS;
    //请求队列
    BlockingQueue<Runnable> workerQueue = new ArrayBlockingQueue<>(50);
    //线程工厂,使用默认的即可
    ThreadFactory threadFactory = Executors.defaultThreadFactory();
    //拒绝策略,如果任务太多处理不过来了,如何拒绝
    RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize
            ,maxPoolSize
            ,keepAliveTime
            ,unit
            ,workerQueue
            ,threadFactory
            ,handler);

    ServerSocket serverSocket = new ServerSocket(port);
    System.out.println("========>>Minicat start on port(多线程):"+port);
    /**
     * 可以请求动态资源
     */
    while (true){
        Socket socket = serverSocket.accept();
        RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
        threadPoolExecutor.execute(requestProcessor);
    }
}

OK ,再次测试,成功~

MINI版Tomcat到此完成。

八,总结

总结一下编写一个MINI版本的Tomcat都需要做些什么:

  1. 定义一个入口类,需要监听的端口号和入口方法——main方法
  2. 定义servlet规范(接口),并实现它——HttpServlet
  3. 编写http协议工具类,主要提供响应头信息
  4. 在main方法中调用start()方法用于启动初始化和请求进来时的操作
  5. 加载解析配置文件(web.xml)
  6. 当请求进来时,解析inputStream,并封装为Request和Response对象。
  7. 判断请求资源的方式(动态资源还是静态资源)
posted @ 2020-12-28 00:36  邓晓晖  阅读(2061)  评论(0编辑  收藏  举报