手写Tomcat之简单实现

139 阅读3分钟

手写Tomcat之简单实现

封装Request对象

/**
 * @author CJ
 * @description 通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法以及URL
 * @create 2021-04-25
 **/
public class Request {
    private String url;
    private String method;

    //通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法以及URL
    public Request(InputStream inputStream) throws IOException {
        String httpRequest = "";
        byte[] bytes = new byte[1024];
        int length = 0;
        if ((length = inputStream.read(bytes)) > 0) {
            httpRequest = new String(bytes, 0, length);
        }

//        GET /executeServlet HTTP/1.1
//        Host: localhost:8888
//        Connection: keep-alive
//        Pragma: no-cache
//        Cache-Control: no-cache
//        sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
//        sec-ch-ua-mobile: ?0
//        DNT: 1
//        Upgrade-Insecure-Requests: 1
//        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36
//        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
//        Sec-Fetch-Site: none
//        Sec-Fetch-Mode: navigate
//        Sec-Fetch-User: ?1
//        Sec-Fetch-Dest: document
//        Accept-Encoding: gzip, deflate, br
//        Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8
//        Cookie: SSO-SESSIONID=88ad2e2270a3498ea896931be14df2b4
        String httpHead = httpRequest.split("\n")[0];

        // GET /executeServlet HTTP/1.1
        url = httpHead.split("\\s")[1];
        method = httpHead.split("\\s")[0];
        System.out.println(this);
    }

//	setter() getter() toString()
}

封装Response对象

/**
 * @author CJ
 * @description 通过输出流,基于HTTP协议对响应内容进行输出
 * @create 2021-04-25
 **/
public class Response {

    private OutputStream outputStream;

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

    /**
     * HTTP响应协议 HTTP/1.1 200 ok
     * HTTP响应内容类型 Content-Type: text/html
     *
     * @param content
     * @throws IOException
     */
    public void write(String content) throws IOException {
        StringBuffer httpResponse = new StringBuffer();
        httpResponse.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: text/html\n")
                .append("\r\n")
                .append("<html><body>")
                .append(content)
                .append("</body></html>");

        outputStream.write(httpResponse.toString().getBytes());
        outputStream.close();
    }
}

自定义Servlet

/**
 * @author CJ
 * @description 自定义简单Servlet
 * @create 2021-04-25
 **/
public abstract class Servlet {
    public abstract void doGet(Request request, Response response);

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

    public void service(Request request, Response response) {
        if (request.getMethod().equalsIgnoreCase("POST")) {
            doPost(request, response);
        } else if (request.getMethod().equalsIgnoreCase("GET")) {
            doGet(request, response);
        }
    }
}

定义具体的Servlet实现

/**
 * @author CJ
 * @description 定义具体的Servlet实现
 * @create 2021-04-25
 **/
public class ExecuteServlet extends Servlet {
    @Override
    public void doGet(Request request, Response response) {
        try {
            response.write("execute get Servlet....");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(Request request, Response response) {
        try {
            response.write("execute post Servlet....");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

定义Servlet映射

public class ServletMapping {
    private String servletName;
    private String url;
    private String clazz;

    public ServletMapping(String servletName, String url, String clazz) {
        this.servletName = servletName;
        this.url = url;
        this.clazz = clazz;
    }

//	setter() getter() toString()
}
/**
 * @author CJ
 * @description Servlet映射关系配置,类似web.xml文件
 * @create 2021-04-25
 **/
public class ServletMappingConfig {

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

    static {
       servletMappingList.add(new ServletMapping("ExecuteServlet", "/executeServlet", "cn.ybzy.tomcat.servlet.ExecuteServlet"));
    }
}

Tomcat入口

public class Tomcat {
    private int prot = 8080;
    private Map<String, String> urlServletMap = new HashMap<String, String>();

    public Tomcat(int prot) {
        this.prot = prot;
    }

    public void start() {
        //初始化Url与对应Servlet的关系
        this.initServletMapping();

        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(prot);
            System.out.println("Tomcat starting ....");

            while (true) {
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();

                Request request = new Request(inputStream);
                Response response = new Response(outputStream);

                //浏览器默认会请求favicon.ico图标
                if (!request.getUrl().equals("/favicon.ico")) {
                    dispatch(request, response);
                    socket.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    private void initServletMapping() {
        List<ServletMapping> servletMappingList = ServletMappingConfig.servletMappingList;
        for (ServletMapping servletMapping : servletMappingList) {
            urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz());
        }
    }

    private void dispatch(Request request, Response response) {
        String clazz = urlServletMap.get(request.getUrl());

        //反射加载对应类
        try {
            Class<Servlet> servlet = (Class<Servlet>) Class.forName(clazz);
            Servlet newInstance = servlet.newInstance();
            newInstance.service(request, response);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

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

}

整体包结构

在这里插入图片描述

执行测试

在这里插入图片描述

连接到目标VM, 地址: ''127.0.0.1:12371',传输: '套接字'', 传输: '{1}'
Tomcat starting ....
Request{url='/executeServlet', method='GET'}
Request{url='/favicon.ico', method='GET'}