從零開始寫一個Tomcat(貳)--建立動態服務器


    上文書說道如何通過http協議建立一個靜態的服務器來訪問靜態網頁,但我們選擇tomcat最主要的原因還是因為它能動態的執行servlet,這邊文章將引導你實現一個能夠運行servlet的服務器,這個簡易的服務器符合tomcat的基本原理,但真的tomcat遠不是這么簡單,即使是15年前的tomcat4。

   1.主程序邏輯分離

    既然要實現動態的服務器,首先我們要實現模式的識別,在tomcat中,tomcat通過讀取web.xml,在servlet的配置中通過url的模式來匹配servlet,我們這里當然不會這么復雜,我們暫且設置如下規則,如果你想訪問一個servlet,那么你就在瀏覽器輸入如localhost/servlet/TestServlet這樣的地址,TestServlet是你servlet的名字,准確點來說就是你的servlet編譯后的class的名稱,當然你可以起你任意喜歡的名字

    以上文為基礎,將main方法中的靜態處理改成如下

 if(request.getUri().startsWith("/servlet/"))
{
       ServletProcessor processor=new ServletProcessor();
        processor.process(request,response);
}else{
        StaticResponseProcessor processor=new StaticResponseProcessor();
         processor.process(request,response);
}

    當request獲得的uri以servlet開頭,就去訪問ServletProccess的方法.

    2.ServletProccess

    這個類的職責就是用於找到servlet並調用.不多說,先上代碼

    public class ServletProcessor {
    public void process(Request request,Response response)
    {
        String uri=request.getUri();
        String servletName=uri.substring(uri.lastIndexOf("/")+1);
        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(repository);
            urls[0] = new URL(null,repository,streamHandler);
            loader=new URLClassLoader(urls);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Class myClass=null;
        try{
            myClass=loader.loadClass(servletName);
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        Servlet servlet=null;

        try{
            servlet=(Servlet)myClass.newInstance();
            servlet.service(request,response);

        }catch (Exception e){
            System.out.println(e.toString());
        }catch (Throwable t){
            System.out.println(t.toString());
        }

    }
}

     首先,我們要找到這個類,這個類應該在哪呢,在tomcat中,這個類即class文件在/WEB-INF/classes下,我們暫且先直接放在webroot下面,負責記錄存儲位置的是一個專門的Constants類的靜態變量

 public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";

  程序使用一個URLClassLoader來加載這個class,為了禁止servlet訪問tomcat中的類,tomcat自定義了一個加載器,這個問題以后再詳細說明,加載完類之后,用newInstance實例化,按理說這里會先調用init方法,老規矩,先忽略了,然后調用servlet的service方法,這時候細心的你可能發現了,在servlet的方法中,我的request是實現了servletRequest接口的,同理還有response,但你的request是自己定義的,這么調用肯定是錯的,而且說好的response.getWriter呢,下面我們來改造request和response

    3.request,response

    其實並不需要多少操作,只需要把request實現ServletRequest接口,response實現ServletResponse接口,然后使用IDE的代碼補全,里面的方法先都空着好了,在response的getWriter添加如下代碼

 public PrintWriter getWriter() throws IOException {
        writer=new PrintWriter(output,true);
        return writer;
    }

 

    4.facade

    至此,這個動態服務器已經可以用了,但是不知道你有沒有注意到另一個問題,我們在servlet使用的request是tomcat傳給我們的,那也就是說我們可以把這個servletRequest向下轉型為tomcat的request類,然后我們就可以訪問request的parse()之類的方法,但顯然tomcat是不容許的,tomcat在這里使用了外觀模式新建一個類requestFacade,在構造函數中傳入tomcat的request,實現所有外部能夠訪問的方法,在方法中調用request的相應的方法,這樣就能完美的隱藏那些request的必須的且不應該被servlet訪問的public方法.

 

 

    5.結果

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("service");
res.getWriter().println("service");
}




地址:https://github.com/Asens/AsServer/tree/master/AsServerV1.0.1

下集預告:我會把接受請求和處理請求的邏輯分離,並引入線程池概念,是這個服務器能夠同時處理多個請求

    

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM