栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【手写一个Tomcat】SimpleTomcat-01

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【手写一个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 {

    
    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();
        }
    }

    
    public String getUrl() {
        return this.url;
    }

    
    public String getMethod() {
        return this.method;
    }
}

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 OKn")
                .append("Content-Type: text/html;n")
                .append("rn")
                .append(s);
        // IO流写出
        this.out.write(sb.toString().getBytes());
    }
}

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;

}

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;


public class SimpleTomcat {

    private int port = 8080;

    private ServerSocket serverSocket;

    private Map 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();
        }
    }

    
    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();
        }
    }

    
    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();
    }
}
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);
    }
}

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);
    }
}

配置

web.properties

servlet.one.url=/firstServlet
servlet.one.className=com.sample.servlet.FirstServlet

servlet.two.url=/secondServlet
servlet.two.className=com.sample.servlet.SecondServlet
文件结构

运行

运行我们的 SimpleTomcat.java

在浏览器中输入

http://localhost:8080/firstServlet

或者

http://localhost:8080/secondServlet

可以看到

参考

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/995293.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号