1. 程式人生 > >[深入剖析Tomcat]一個簡單的servlet容器實現

[深入剖析Tomcat]一個簡單的servlet容器實現

本文為讀《深入剖析Tomcat》第2章做的筆記、寫的程式碼、做的分析。作者是菜鳥,謹慎參考

  1. 實現流程圖
    這裡寫圖片描述

  2. HttpServer類:

public class HttpServer1 {
    private static String SHUT_DOWN="shutdown";
    public static void main(String[] args){
        HttpServer1 server=new HttpServer1();
        server.await();
    }
    private void await() {
        ServerSocket server=null
; int port=806; try { server=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); } catch (Exception e) { e.printStackTrace(); } boolean shutdown=false; while(!shutdown){ Socket socket; InputStream input; OutputStream out
; try { socket = server.accept(); input=socket.getInputStream(); out=socket.getOutputStream(); Request req=new Request(input); req.parse(); Response res=new Response(out); res.setRequest(req); if
(req.getUri().startsWith("/servlet/")){ ServletProcessor1 processor=new ServletProcessor1(); processor.process(req,res); }else{ StaticResourceProcessor processor=new StaticResourceProcessor(); processor.process(req, res); } socket.close(); shutdown=req.getUri().equals(SHUT_DOWN); } catch (IOException e) { e.printStackTrace(); } } } }

3 . Response類的主要方法

public class Response implements ServletResponse {
    private OutputStream out;
    private Request req;
    private static int BUFFER_SIZE = 1024;
    public Response(OutputStream out) {
        this.out = out;
    }
    public void setRequest(Request req) {
        this.req = req;
    }
    public void sendStaticReSource() throws IOException {
        System.out.println("訪問的資源為"+Constants.WEB_ROOT + req.getUri());
        File file = new File(Constants.WEB_ROOT + req.getUri());
        FileInputStream fis = null;
        byte[] buffer = new byte[1024];
        int n = -1;
        try {
            if (file.exists()) {
                System.out.println("該資源存在");
                fis = new FileInputStream(file);
                n = fis.read(buffer, 0, BUFFER_SIZE);
                while (n != -1) {
                    out.write(buffer, 0, n);
                    n = fis.read(buffer, 0, BUFFER_SIZE);
                }

            } else {
                System.out.println("該資源不存在");
                String msg = "msg error! file does not exist.";
                out.write(msg.getBytes());
            }
        } catch (IOException e) {
        }finally{
            if(fis!=null)
                fis.close();
        }
    }
    public PrintWriter getWriter() throws IOException{
        //呼叫println會自動重新整理,呼叫print不會自動重新整理
        PrintWriter writer =new PrintWriter(out,true);
        return writer;
    }
}

4 . Request的主要方法:

public class Request implements ServletRequest{
    private final static int BUFFER_SIZE=2048;
    private InputStream input;
    private String uri;
    public Request(InputStream input) {
        this.input=input;
    }
    public void parse() {
        byte[] buffers=new byte[BUFFER_SIZE];
        int n;
        try {
            n = input.read(buffers, 0, BUFFER_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
            n=-1;
        }
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<n;i++){
            sb.append((char)buffers[i]);
        }
        System.out.println("解析得到request:");
        System.out.println(sb);
        uri=parseUri(sb.toString());
        System.out.println("解析request中的uri:"+uri);
    }
    private String parseUri(String str){
        int index1,index2;
        index1=str.indexOf(' ');
        while(index1!=-1){
            index2=str.indexOf(' ', index1+1);
            if(index2-index1>0)
                return str.substring(index1+1,index2);
        }
        return null;
    }
    public  String getUri(){
        return uri;
    }
}
  1. ServletProcessor類:
public class ServletProcessor1 {
    public void process(Request req,Response res){
        String uri =req.getUri();
        String servletName=uri.substring(uri.lastIndexOf('/')+1);
        //URLCLassLoader為ClassLoader的直接子類
        //用它的loadClass()方法來載入servlet類
        URLClassLoader loader=null;
        try {
        URL[] urls=new URL[1];
        URLStreamHandler streamHandler=null;
        File classPath=new File(Constants.WEB_ROOT);
        String repository=(new URL("file",null,classPath.getCanonicalPath()
                +File.separator)).toString();
        System.out.println("servlet的目錄repository:"+repository);
        //使用streamHandler的因為URL過載中,第3個引數不同
        //streamHandler將其區分,repository為servlet的目錄
        urls[0]=new URL(null,repository,streamHandler);
        //urls為URL物件陣列,每個URL物件都指明瞭類載入器在哪裡查詢類
        //若URL以“/”結尾,則表明其指向一個目錄,否則為指向一個jar檔案
        //根據需要載入器會下載這個jar檔案
        //在servlet容器中,類載入器查詢servlet類的目錄稱作倉庫(repository)
        loader=new URLClassLoader(urls);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Class myclass=null;

        try {
            System.out.println("需要載入的 servletName:"+servletName);
            myclass=loader.loadClass(servletName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Servlet servlet=null;
        try {
            servlet=(Servlet) myclass.newInstance();
            servlet.service(req, res);
        }  catch (Exception e) {
            e.printStackTrace();
        }

    }
}

6 . 需要注意的兩點:

1)第一點需要注意的:在ServletProcessor類的process(Request req,Response res)中如下寫是不好的,因為在service(ServletRequest req, ServletResponse res)方法中,可以將req下轉型為Request型,從而req可以訪問parse方法,可以將res下轉型為Request型,從而res可以訪問sendStaticResource()方法:

    servlet=(Servlet) myclass.newInstance();
            servlet.service(req, res);

2)針對上一點的改進方法,增加ResponseFacade類如下圖:
這裡寫圖片描述

3)將上1)步的程式碼改為如下,為什麼這樣能避免不好的情況,在下一篇文章中舉例說明:

            servlet=(Servlet) myclass.newInstance();
            RequestFacade requestFacade=new RequestFacade(req);
            ResponseFacade responseFacade=new ResponseFacade(res);
            servlet.service(requestFacade, responseFacade);

4)第2個需要注意的問題,如下圖,ServletResponse為介面,3)中servlet.service(requestFacade, responseFacade)會呼叫被訪問servletName類的service(ServletRequest req, ServletResponse res)方法,例如儘管res是ServletResponse 例項,當使用res.getWriter(),實際上呼叫的是Response類中重寫的getWriter():
這裡寫圖片描述