1. 程式人生 > >手寫一個Tomcat

手寫一個Tomcat

(參考了公眾號:java架構沉思錄中的文章:教你寫一個迷你版的toncat 原文:https://www.jianshu.com/p/dcelee01fb90) 作為一個java學習的起步者,對tomcat的認識還是有很多的欠缺,在無意中發現了這篇文章,便在自己的環境下嘗試搭建,收穫良多: 分以下幾個步驟: (1)提供Socket服務 (2)進行請求的轉發 (3)把請求和響應封裝成request/response 程式碼實現如下: 1、工程截圖: 在這裡插入圖片描述 2、封裝請求物件:通過輸入流,對HTTP協議進行解析,拿到了HTTP請求頭的方法和URL:


/**
 * @author wangjie
 * @version 2018/11/9
 * 封裝請求物件
 * 通過輸入流,對http協議進行解析,拿到http請求頭的方法和url
 */
public class MyRequest {
    private String url;
    private String method;

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

        String httpHead = httpRequest.split("\n")[0];
        url=httpHead.split("\\s")[1];
        method=httpHead.split("\\s")[0];
        System.out.println(this);
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
}

3、封裝響應物件:基於HTTP協議的格式進行輸出寫入。

/**
 * @author wangjie
 * @version 2018/11/9
 * 封裝響應物件
 * 基於HTTP協議的格式進行輸出寫入。
 */
public class MyResponse {
    private OutputStream outputStream;

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

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

4、servlet請求處理基類:Tomcat是滿足Servlet規範的容器,所以Tomcat需要提供API:doGet/doPost/service。

/**
 * @author wangjie
 * @version 2018/11/9
 * Servlet請求處理基類
 */
public abstract class MyServlet {
    public abstract void doGet(MyRequest myRequest,MyResponse myResponse);
    public abstract void doPost(MyRequest myRequest,MyResponse myResponse);
    public void service(MyRequest myRequest,MyResponse myResponse){
        if(myRequest.getMethod().equalsIgnoreCase("POST")){
            doPost(myRequest,myResponse);
        }else if(myRequest.getMethod().equalsIgnoreCase("GET")){
            doGet(myRequest,myResponse);
        }
    }
}

5、Servlet實現類:提供2個實現類,用於測試。

/**
 * @author wangjie
 * @version 2018/11/9
 * servlet實現類
 */
public class FindGirlServlet extends MyServlet{
    @Override
    public void doGet(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("get gril....");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    @Override
    public void doPost(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("post girl...");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}
/**
 * @author wangjie
 * @version 2018/11/9
 */
public class HelloWorldServlet extends MyServlet {
    @Override
    public void doGet(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("get world...");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

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

6、Servlet配置:對比之前在web開發中,會在web.xml中通過和指定哪個URL交給哪個servlet來處理。

/**
 * @author wangjie
 * @version 2018/11/9
 * 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;
    }

    public String getServletName() {
        return servletName;
    }
    public void setServletName(String servletName) {
        this.servletName = servletName;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getClazz() {
        return clazz;
    }
    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
}
/**
 * @author wangjie
 * @version 2018/11/9
 */
public class ServletMappingConfig {
    public static List<ServletMapping> servletMappingList =new ArrayList<>();
    //制定哪個URL交給哪個servlet來處理
    static{
        servletMappingList.add(new ServletMapping("findGirl","/girl","wj.mytomcat.FindGirlServlet"));
        servletMappingList.add(new ServletMapping("helloWorld","/world","wj.mytomcat.HelloWorldServlet"));
    }
}

7、啟動類: tomcat的處理流程:把URL對應處理的Servlet關係形成,解析HTTP協議,封裝請求/響應物件,利用反射例項化具體的Servlet進行處理。

/**
 * @author wangjie
 * @version 2018/11/9
 * tomcat啟動類
 */
public class MyTomcat {
    private int port=8088;
    private Map<String,String> urlServletMap =new HashMap<String,String>();
    public MyTomcat(int port){
        this.port=port;
    }

    public void start(){
//        初始化URL與對應處理的servlet的關係
        initServletMapping();

        ServerSocket serverSocket=null;
        try{
            serverSocket = new ServerSocket(port);
            System.out.println("MyTomcat is start...");

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

                MyRequest myRequest= new MyRequest(inputStream);
                MyResponse myResponse =new MyResponse(outputStream);

//                請求分發
                dispatch(myRequest,myResponse);
                socket.close();
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (null != serverSocket){
                try{
                    serverSocket.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

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

    public void dispatch(MyRequest myRequest,MyResponse myResponse){
        String clazz =urlServletMap.get(myRequest.getUrl());

        //反射
        try{
            Class<MyServlet> myServletClass =(Class<MyServlet>) Class.forName(clazz);
            MyServlet myServlet= myServletClass.newInstance();

            myServlet.service(myRequest,myResponse);
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        new MyTomcat(8088).start();
    }
}

8、測試: 執行專案後,在瀏覽器輸入:localhost:8088/girl 在這裡插入圖片描述 在瀏覽器輸入:localhost:8088/world 在這裡插入圖片描述 實踐完成。