手写迷你版Tomcat,了解Tomcat执行流程

1.封装请求对象

package tomcat.http;

import lombok.Getter;
import lombok.ToString;

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

/**
 * 通过输入流,对HTTP协议进行解析,拿到HTTP请求头的方法以及URL。
 *
 * @author Manaphy
 */
@Getter
@ToString
public class MyRequest {
    private final String url;
    private final String method;

    public MyRequest(InputStream inputStream) throws IOException {
        String httpRequest = "";
        byte[] httpRequestBytes = new byte[1024];
        int length;
        if ((length = inputStream.read(httpRequestBytes)) > 0) {
            httpRequest = new String(httpRequestBytes, 0, length);
        }


//        HTTP请求协议
//        GET /favicon.ico HTTP/1.1
//        Accept: */*
//        Accept-Encoding: gzip, deflate
//        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36
//        Connection: keep-alive

        String httpHead = httpRequest.split("\n")[0];
        url = httpHead.split("\\s")[1];
        method = httpHead.split("\\s")[0];
        System.out.println(this);
    }
}

2.封装响应对象

package tomcat.http;

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

/**
 * 基于HTTP协议的格式进行输出写入。
 *
 * @author Manaphy
 */
public class MyResponse {
    private final OutputStream outputStream;

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

    public void write(String content) throws IOException {
        /*
        HTTP响应协议
        HTTP/1.1 200 OK
        Content-Type: text/html

        <html><body></body></html>
         */
        String httpResponse = "HTTP/1.1 200 OK\n" +
                "Content-Type: text/html\n" +
                "\r\n" +
                "<html><body>" +
                content +
                "</body></html>";
        outputStream.write(httpResponse.getBytes());
        outputStream.close();
    }
}

3. Servlet 请求处理基类

package tomcat.http;

import java.io.IOException;

/**
 * Servlet 请求处理基类
 *
 * @author Manaphy
 */
public abstract class AbstractServlet {

    private static final String POST = "POST";

    private static final String GET = "GET";

    /**
     * get请求
     *
     * @param myRequest  我的请求
     * @param myResponse 我的响应
     * @throws IOException IOException
     */
    public abstract void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException;

    /**
     * post请求
     *
     * @param myRequest  我的请求
     * @param myResponse 我的响应
     * @throws IOException IOException
     */
    public abstract void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException;

    public void service(MyRequest myRequest, MyResponse myResponse) throws IOException {
        if (POST.equalsIgnoreCase(myRequest.getMethod())) {
            doPost(myRequest, myResponse);
        } else if (GET.equalsIgnoreCase(myRequest.getMethod())) {
            doGet(myRequest, myResponse);
        }
    }
}

4.Servlet 实现类

package tomcat.servlet;

import tomcat.http.AbstractServlet;
import tomcat.http.MyRequest;
import tomcat.http.MyResponse;

import java.io.IOException;

/**
 * 测试用的实现类
 *
 * @author Manaphy
 */
public class FindGirlServlet extends AbstractServlet {
    @Override
    public void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException {
        myResponse.write("get girl...");
    }

    @Override
    public void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException {
        myResponse.write("post girl...");
    }
}
package tomcat.servlet;

import tomcat.http.AbstractServlet;
import tomcat.http.MyRequest;
import tomcat.http.MyResponse;

import java.io.IOException;

/**
 * 测试用的实现类
 *
 * @author Manaphy
 */
public class HelloWorldServlet extends AbstractServlet {
    @Override
    public void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException {
        myResponse.write("get world...");
    }

    @Override
    public void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException {
        myResponse.write("post world...");
    }
}

5.Servlet 配置

package tomcat.servlet;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

/**
 * web.xml中servlet的对象
 *
 * @author Manaphy
 */
@Getter
@Setter
@AllArgsConstructor
public class ServletMapping {
    private String servletName;
    private String url;
    private String clazz;
}
package tomcat.servlet;

import java.util.ArrayList;
import java.util.List;

/**
 * servlet映射配置
 * 我们在servlet开发中,会在web.xml中通过<servlet></servlet>
 * 和<servlet-mapping></servlet-mapping>来进行指定哪个URL交给哪个servlet进行处理。
 *
 * @author Manaphy
 */
public class ServletMappingConfig {

    public static List<ServletMapping> servletMappingList = new ArrayList<>();

    static {
        servletMappingList.add(new ServletMapping("findGirl", "/girl", "tomcat.servlet.FindGirlServlet"));
        servletMappingList.add(new ServletMapping("helloWorld", "/world", "tomcat.servlet.HelloWorldServlet"));
    }
}

6.主启动类

package tomcat;

import tomcat.http.AbstractServlet;
import tomcat.http.MyRequest;
import tomcat.http.MyResponse;
import tomcat.servlet.ServletMapping;
import tomcat.servlet.ServletMappingConfig;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * Tomcat主程序
 * 1.提供Socket服务
 * 2.进行请求的分发
 * 3.需要把请求和响应封装成request/response
 *
 * @author Manaphy
 */
public class TomcatMain {
    private int port = 8080;
    private final Map<String, AbstractServlet> urlServletMap = new HashMap<>();

    public TomcatMain(int port) {
        this.port = port;
    }

    public TomcatMain() {
    }

    public void start() {
        //初始化 URL 与对应处理的 servlet 的关系
        initServletMapping();

        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Tomcat started on port(s): " + port);
            while (true) {
                Socket socket = serverSocket.accept();
                //请求分发
                dispatch(socket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void initServletMapping() {
        try {
            for (ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
                String url = servletMapping.getUrl();
                AbstractServlet servlet = (AbstractServlet) Class.forName(servletMapping.getClazz()).newInstance();
                urlServletMap.put(url, servlet);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void dispatch(Socket socket) {
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) {
            MyRequest myRequest = new MyRequest(inputStream);
            MyResponse myResponse = new MyResponse(outputStream);

            String url = myRequest.getUrl();
            if (urlServletMap.containsKey(url)) {
                urlServletMap.get(url).service(myRequest, myResponse);
            } else {
                myResponse.write("404 NOT FOUNT");
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new TomcatMain().start();
    }
}

7.测试

在这里插入图片描述
在这里插入图片描述
控制台输出
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值