• 【手写一个Tomcat】SimpleTomcat-01


    在这里插入图片描述

    前言

    本文实现一个简易Tomcat,遵循【Tomcat】第八篇:150代码手写Tomcat…

    实现

    http.TomcatRequest

    TomcatRequest.java

    package com.sample.http;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class TomcatRequest {
    
        /**
         * 请求方法 get post delete put
         */
        private String method;
    
        private String url;
    
        public TomcatRequest(InputStream in) {
            try {
                // 1.content用来保存InputStream中的http请求信息
                String content = "";
                byte[] buff = new byte[1024];
                int len = 0;
                if ((len = in.read(buff)) > 0) {
                    content = new String(buff, 0, len);
                }
    
                // 2.对http请求信息进行处理,得到Method与Url
                String line = content.split("\\n")[0];
                String[] arr = line.split("\\s");
                this.method = arr[0];
                this.url = arr[1].split("\\?")[0];
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 返回url
         * @return String
         */
        public String getUrl() {
            return this.url;
        }
    
        /**
         * 返回请求方法
         * @return String
         */
        public String getMethod() {
            return this.method;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    http.TomcatResponse

    TomcatResponse.java

    package com.sample.http;
    
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class TomcatResponse {
    
        private OutputStream out;
    
        public TomcatResponse(OutputStream out) {
            this.out = out;
        }
    
        public void write(String s) throws IOException {
            StringBuilder sb = new StringBuilder();
            // 因为写出的内容要被http协议解析,所以要符合http协议规范,有其要求的响应头(主要是状态码和响应格式)
            sb.append("HTTP/1.1 200 OK\n")
                    .append("Content-Type: text/html;\n")
                    .append("\r\n")
                    .append(s);
            // IO流写出
            this.out.write(sb.toString().getBytes());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    http.TomcatServlet

    TomcatServlet.java

    package com.sample.http;
    
    import java.io.IOException;
    
    public abstract class TomcatServlet {
    
        // 这里的request与response都是Tomcat对象创建好然后传进来的
        public void service(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
            if ("GET".equalsIgnoreCase(tomcatRequest.getMethod())) {
                doGet(tomcatRequest, tomcatResponse);
            }
            else {
                doPost(tomcatRequest, tomcatResponse);
            }
        }
    
        // 这里是模板方法模式,交给子类去具体实现
        protected abstract void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;
    
        protected abstract void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    SimpleTomcat

    SimpleTomcat.java

    package com.sample;
    
    import com.sample.http.TomcatRequest;
    import com.sample.http.TomcatResponse;
    import com.sample.http.TomcatServlet;
    
    import java.io.FileInputStream;
    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;
    import java.util.Properties;
    
    /**
     * Tomcat核心类
     */
    public class SimpleTomcat {
    
        private int port = 8080;
    
        private ServerSocket serverSocket;
    
        private Map<String, TomcatServlet> servletMapping = new HashMap<>();
    
        private Properties webxml = new Properties();
    
        private void init() {
            try {
                // 1.加载web.properties文件
                String WEB_INF = this.getClass().getResource("/").getPath();
                FileInputStream fileInputStream = new FileInputStream(WEB_INF + "web.properties");
                webxml.load(fileInputStream);
    
                // 2.遍历配置文件,寻找url与servlet映射关系配置
                for (Object o : webxml.keySet()) {
                    String key = o.toString();
                    // 以url结尾的key就是要映射的路径,下面是两条配置示例:
                    // servlet.one.url=/firstServlet.do
                    // servlet.one.className=com.yzh.tomcat.servlet.FirstServlet
                    if (key.endsWith(".url")) {
                        // 去掉.url就是servlet的name(servlet.one)
                        String servletName = key.replaceAll("\\.url$", "");
    
                        // 2.1 获取到url(/first.do)
                        String url = webxml.getProperty(key);
    
                        // 2.2 获取对应servlet全类名(com.yzh.tomcat...FirstServlet),并通过反进行实例化
                        String className = webxml.getProperty(servletName + ".className");
                        // 注:这里是将所有Servlet都强转为TomcatServlet,所以一定要继承TomcatServlet
                        TomcatServlet tomcatServlet = (TomcatServlet) Class.forName(className).newInstance();
    
                        // 3.将url与servlet实例保存到servletMapping中(单例模式)
                        servletMapping.put(url, tomcatServlet);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 启动tomcat
         */
        public void start() {
            // 1.调用init,目的是得到servletMapping的映射关系
            init();
    
            try {
                // 2.通过BIO创建socket的服务端,在指定端口开始监听
                serverSocket = new ServerSocket(this.port);
                System.out.println("SimpleTomcat已启动,监听的端口是" + this.port);
    
                // 3.用一个死循环持续等待并处理用户请求
                while (true) {
                    Socket client = serverSocket.accept();
                    // process是具体处理请求的逻辑,参数是当前连接的Socket
                    process(client);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 具体处理请求
         * 1.创建IO流,并包装成Request与Response
         * 2.获取请求Url,取出对应Servlet进行处理
         */
        private void process(Socket client) throws IOException {
            // 1.获取IO流,并封装成Request与Response
            InputStream in = client.getInputStream();
            OutputStream out = client.getOutputStream();
            // 注:这里要明白,每次请求的Request和Response都是不同的(因为连接时的socket不同),他俩的作用域仅为当前会话
            TomcatRequest request = new TomcatRequest(in);
            TomcatResponse response = new TomcatResponse(out);
    
            // 获取请求URL,寻找相应Servlet进行处理
            String url = request.getUrl();
            // 2.判断改url是否有对应的Servlet实例
            if (servletMapping.containsKey(url)) {
                // 如果有,调用service方法进行处理
                servletMapping.get(url).service(request, response);
            }
            else {
                // 如果没有,写出404
                response.write("404 - Not Found");
            }
    
            // 3.关闭本次连接相关资源
            out.flush();
            out.close();
            in.close();
            client.close();
        }
    
        public static void main(String[] args) {
            new SimpleTomcat().start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    servlet.FirstServlet

    package com.sample.servlet;
    
    import com.sample.http.TomcatRequest;
    import com.sample.http.TomcatResponse;
    import com.sample.http.TomcatServlet;
    
    import java.io.IOException;
    
    public class FirstServlet extends TomcatServlet {
    
        @Override
        protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
            tomcatResponse.write("this is FirstServlet!");
        }
    
        @Override
        protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
            doPost(tomcatRequest, tomcatResponse);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    servlet.SecondServlet

    SecondServlet.java

    package com.sample.servlet;
    
    
    import com.sample.http.TomcatRequest;
    import com.sample.http.TomcatResponse;
    import com.sample.http.TomcatServlet;
    
    import java.io.IOException;
    
    public class SecondServlet extends TomcatServlet {
    
        @Override
        protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
            tomcatResponse.write("Hello world!");
        }
    
        @Override
        protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
            doPost(tomcatRequest, tomcatResponse);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    配置

    web.properties

    servlet.one.url=/firstServlet
    servlet.one.className=com.sample.servlet.FirstServlet
    
    servlet.two.url=/secondServlet
    servlet.two.className=com.sample.servlet.SecondServlet
    
    • 1
    • 2
    • 3
    • 4
    • 5

    文件结构

    在这里插入图片描述

    运行

    运行我们的 SimpleTomcat.java
    在这里插入图片描述

    在浏览器中输入

    http://localhost:8080/firstServlet
    
    • 1

    或者

    http://localhost:8080/secondServlet
    
    • 1

    可以看到

    在这里插入图片描述

    参考

    https://blog.csdn.net/weixin_43935927/article/details/108743213

  • 相关阅读:
    MySQL 事务隔离级别和MVCC版本控制
    JVM栈与堆(一)之栈和栈中单位栈帧
    LibTorch | 使用神经网络求解一维稳态对流扩散方程
    计算机网络——网络层(概念及IP地址划分)
    OpenCV C++ Look Up Table(查找表)
    Spring Cloud 全链路日志traceId
    剑指Offer10- I. 斐波那契数列
    C/C++预定义宏、 #line 、#error、 #pragma和泛型选择
    选择和操作元素
    2023-9-23 区间分组
  • 原文地址:https://blog.csdn.net/fisherish/article/details/125474805