從零手動實現簡易Tomcat

Mr_初晨發表於2019-01-30
Hello大家好,本章我們自己實現一個簡易版的tomcat 。有問題可以聯絡我mr_beany@163.com。另求各路大神指點,感謝

程式的執行少不了伺服器的支援,而tomcat因其效能穩定,免費等優點,深受 Java 愛好者的喜愛並得到了部分軟體開發商的認可,為目前比較流行的 Web 應用伺服器。

那麼到底它是怎麼執行的呢?今天我們來實現一個簡化版tomcat來感受一下。

一:匯入解析xml的jar包

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>複製程式碼

二:編寫XML解析類

package com.example.demo.utils;

import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * @author zy
 */
public class XmlUtils {
    /**
     * 定義解析器和文件物件
     */
    public SAXReader saxReader;
    public Document document;

    public XmlUtils(String path) {
        //獲取解析器
        saxReader = new SAXReader();
        try {
            //獲取文件物件
            document = saxReader.read(path);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取節點下的所有節點
     *
     * @param name
     * @return
     */
    public List<Element> getNodes(String name) {
        Element root = document.getRootElement();
        return root.elements(name);
    }
}
複製程式碼

三:實現Request和Response

其中Request是對瀏覽器的請求的封裝,而Response是對瀏覽器請求的響應,換而言之就是Request 用來取出請求資訊,而 Response 則用來新增要返回給瀏覽器的資訊。

建立Request.java

package com.example.demo.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author zy
 */
public class Request {

    private String method;

    private String url;

    public Request(InputStream inputStream) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String[] methodAndUrl = bufferedReader.readLine().split(" ");
        this.method = methodAndUrl[0];
        this.url = methodAndUrl[1];
        System.out.println("請求型別:"+ method);
        System.out.println("請求路徑:"+ url);
    }

    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;
    }
    
}複製程式碼

建立Response.java

package com.example.demo.http;

import java.io.OutputStream;

/**
 * @author zy
 */
public class Response {

    private OutputStream outputStream;

    private String write;

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

    public String getWrite() {
        return write;
    }

    public void setWrite(String write) {
        this.write = write;
    }

}複製程式碼

四:實現Servlet

先做個介面

package com.example.demo.servlet;

import com.example.demo.http.Request;
import com.example.demo.http.Response;

/**
 * @author zy
 */
public abstract class AbstractServlet {

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

    public abstract void doPost(Request request, Response response);
}複製程式碼

再來實現自己的Servlet

package com.example.demo.servlet;

import com.example.demo.http.Request;
import com.example.demo.http.Response;

/**
 * @author zy
 */
public class FirstServlet extends AbstractServlet {

    @Override
    public void doGet(Request request, Response response) {
        response.setWrite("我的第一個Servlet");
    }

    @Override
    public void doPost(Request request, Response response) {
        this.doGet(request, response);
    }
}複製程式碼

五:建立web.xml

這裡配置了請求路徑的servlet的對應關係,檔案放在resources資料夾下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

    <servlet>
        <servlet-name>first.html</servlet-name>
        <servlet-class>com.example.demo.servlet.FirstServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>first.html</servlet-name>
        <url-pattern>/first.html</url-pattern>
    </servlet-mapping>

</web-app>複製程式碼

六,建立tomcat,初始化配置檔案

package com.example.demo.tomcat;

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

import com.example.demo.utils.XmlUtils;
import org.dom4j.Element;

/**
 * @author zy
 */
public class Tomcat {
    /**
     * 設定埠號
     */
    private static final int PORT = 8080;

    public static final HashMap<String, Object> SERVLET_MAPPING = new HashMap<>();

    public static final HashMap<String, Object> SERVLET = new HashMap<>();

    /**
     * 控制伺服器啟動關閉
     */
    public boolean tomcatStarBool = true;

    private void init() {
        InputStream io = null;
        try {
            System.out.println("載入配置檔案開始");
            //讀取配置檔案
            XmlUtils xml = new XmlUtils(XmlUtils.class.getResource("/")+"web.xml");
            //將所有的類都儲存到容器中
            List<Element> list = xml.getNodes("servlet");
            for (Element element : list) {
                SERVLET.put(element.element("servlet-name").getText(),
                        Class.forName(element.element("servlet-class").getText()).newInstance());
            }
            //對映關係建立
            List<Element> list2 = xml.getNodes("servlet-mapping");
            for (Element element : list2) {
                SERVLET_MAPPING.put(element.element("url-pattern").getText(),
                        element.element("servlet-name").getText());
            }
            System.out.println("載入配置檔案結束");
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (io != null) {
                try {
                    io.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

}複製程式碼

七:建立處理請求任務的SocketProcess

package com.example.demo.tomcat;

import com.example.demo.http.Request;
import com.example.demo.http.Response;
import com.example.demo.servlet.AbstractServlet;

import java.io.OutputStream;
import java.net.Socket;

/**
 * @author zy
 */
public class SocketProcess extends Thread{

    protected Socket socket;

    public SocketProcess(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            Request request = new Request(socket.getInputStream());
            Response response = new Response(socket.getOutputStream());
            String servletName = (String) Tomcat.SERVLET_MAPPING.get(request.getUrl());
            if(servletName!=null && !servletName.isEmpty()) {
                //對映有的話找到對應的物件
                AbstractServlet servlet = (AbstractServlet) Tomcat.SERVLET.get(servletName);
                if(servlet!=null) {
                    servlet.doGet(request, response);
                }else {
                    System.out.println("找不到對應的servlet");
                }
            }else {
                System.out.println("找不到對應的servletMapping");
            }
            String res = response.getWrite();
            OutputStream outputStream = socket.getOutputStream();

            outputStream.write(res.getBytes("GBK"));
            outputStream.flush();
            outputStream.close();

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}複製程式碼

八:在tomcat.java中新增啟動方法

private void start() {
    try {
        ServerSocket serverSocket = new ServerSocket(PORT);
        System.out.println("Tomcat 服務已啟動,地址:localhost ,埠:" + PORT);
        this.init();
        //持續監聽
        do {
            Socket socket = serverSocket.accept();
            //處理任務
            Thread thread = new SocketProcess(socket);
            thread.start();
        } while (tomcatStarBool);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Tomcat tomcat = new Tomcat();
    tomcat.start();
}複製程式碼

九:測試

啟動在tomcat.java中執行void main方法

從零手動實現簡易Tomcat

出現如下字樣代表啟動成功,然後位址列中輸入

http://localhost:8080/first.html

?????為什麼是這樣。。。。。。從零手動實現簡易Tomcat

啊!原來沒有新增響應頭資訊

Response.java中加入

/**
 * 響應頭資訊
 */
public static final String RESPONSE_HEADER ="HTTP/1.1 200 \r\n"
        + "Content-Type: text/html\r\n"
        + "\r\n";複製程式碼

SocketProcess.java中

String res = response.getWrite();複製程式碼

改成

String res = Response.RESPONSE_HEADER + response.getWrite();複製程式碼

重新啟動,訪問

從零手動實現簡易Tomcat



結尾

祝大家新年快樂。感謝支援!


其他文章:

從零搭建自己的springboot後臺框架

碼雲地址: gitee.com/beany/mySpr…

GitHub地址: github.com/MyBeany/myS…


相關文章