1. 程式人生 > >tomcat學習(2) 一個簡單的servlet容器

tomcat學習(2) 一個簡單的servlet容器

servlet容器

2.1 javax.servlet.Servlet介面

    servlet程式設計需要使用到java.servlet和javax.servlet.http兩個包下的介面和類,在所有的類和介面中,java.servlet.servlet介面是最重要的,所有的servlet程式都必須實現該介面或者繼承實現該介面的類。

介面必須實現的五個方法

    

Servlet介面需要實現下面的5個方法:

         public void init(ServletConfig config) throws ServletException

         public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException

         public void destroy()

         public ServletConfig getServletConfig()

         public java.lang.String getServletInfo()

    

    在Servlet介面中宣告的五個方法裡,init(),service(),destory()方法都是servlet生命週期的相關方法,當例項化某個servlet類後,servlet容器會呼叫其init()方法進行初始化,servlet容器只會呼叫該方法一次,呼叫後則可以執行服務方法。在servlet接收任何請求前,必須是經過正確初始化的。servlet程式可以重寫此方法。

    當servlet的一個客戶端請求到達後,servlet容器就呼叫響應的servlet的service()方法,並將javax.servlet.servletRequest物件和javax.servlet.servletResponse物件作為引數傳入。servletRequest物件包含客戶端HTTP請求的資訊,servletResponse物件則封裝servlet的響應資訊。在servlet生命週期,service()可能會被多次呼叫。


    在將servlet例項從服務中移除前,會呼叫servlet例項的destroy方法。一般情況下,在伺服器關閉前,會發生上述情況,servlet容器會釋放記憶體。只有當servlet例項的service方法中所有的執行緒都退出或執行超時後,才會呼叫destroy方法。當容器呼叫了destroy方法精闢,就不會再呼叫service方法了。

2.2 javax.servlet.servlet介面

下面從servlet容器的角度觀察servlet的開發。在一個全功能servlet容器中,對servlet的每個HTTP請求來說,容器要做下面幾件事:

         當第一次呼叫servlet時,要載入servlet類,呼叫init方法(僅此一次);

         針對每個request請求,建立一個Request物件和一個Resposne物件;

         呼叫相應的servlet的service方法,將Request物件和Response物件作為引數傳入;

         當關閉servlet時,呼叫destroy方法,並解除安裝該servlet類。

這裡建立的servlet容器是一個很小的容器,沒有實現所有的功能。因此,它僅能執行非常簡單的servlet類,無法呼叫servlet的init和destroy方法。它能執行功能如下所示:

         等待HTTP請求;

         建立Request和Response物件;

         若請求的是一個靜態資源,則呼叫StaticResourceProcessor物件的process方法,傳入request和response物件;

         若請求的是servlet,則載入相應的servlet類,呼叫service方法,傳入request物件和response物件。

注意,在這個servlet中,每次請求servlet都會載入servlet類。


大致的流程如下




2.2.1  HttpServer1類

         程式碼清單如下:

該類與第一章的HttpServer類類似,只是完善了對靜態資源和動態資源的處理。

2.2.2  Request類

         程式碼清單如下:

該類實現了javax.servlet.ServletRequest介面,但並不返回實際內容。

2.2.3  Response類

實現了javax.servlet.ServletResponse介面,大部分方法都返回一個空值,除了getWriter方法以外。

        

         在getWriter方法中,PrintWriter類的建構函式的第二個引數表示是否啟用autoFlush。因此,若是設定為false,則如果是servlet的service方法的最後一行呼叫列印方法,則該列印內容不會被髮送到客戶端。這個bug會在後續的版本中修改。

2.2.4  StaticResourceProcessor類

該類用於處理對靜態資源的請求。

2.2.5  ServletProcessor1類

         該類用於處理對servlet資源的請求。

 

    

該類很簡單,只有一個process方法。載入servlet時使用的是UrlClassLoader類,它是ClassLoader類的直接子類,有三種構造方法。


2.3  Application 2

在之前的程式中,有一個嚴重的問題,必須將ex02.pyrmont.Request和ex02.pyrmont.Response分別轉型為javax.servlet.ServletRequest和javax.servlet.ServletResponse,再作為引數傳遞給具體的servlet的service方法。這樣並不安全,熟知servlet容器的人可以將ServletRequest和ServletResponse類向下轉型為Request和Response類,並執行parse和sendStaticResource方法。

         一種解決方案是將這兩個方法的訪問修飾符改為預設的(即,default),這樣就可以避免包外訪問。另一種更好的方案是使用外觀設計模式。uml圖如下:


在第二個應用程式中,添加了兩個façade類,RequestFacade和ResponseFacade。RequestFacade類實現了ServletRequest介面,通過在其構造方法中傳入一個ServletRequest型別引用的Request物件來例項化。ServletRequest介面中每個方法的實現都會呼叫Request物件的相應方法。但是,ServletRequest物件本身是private型別,這樣就不能從類的外部進行訪問。這裡也不再將Request物件向上轉型為ServletRequest物件,而是建立一個RequestFacade物件,並把它傳給service方法。這樣,就算是將在servlet中獲取了ServletRequest物件,並向下轉型為RequestFacade物件,也不能再訪問ServletRequest介面中的方法了,就可以避免前面所說的安全問題。

         RequestFacade.java程式碼如下:

注意它的建構函式,接收一個Request物件,然後向上轉型為ServletRequest物件,賦給其private成員變數request。該類的其他方法中,都是呼叫request的相應方法實現的,這樣就將ServletRequest完整的封裝得RequestFacade中了。

         同理,ResponseFacade類也是這樣的。

         Application 2中的類包括,HttpServer2、Request、Response、StaticResourceProcessor、ServletProcessor2、Constants。



原始碼路徑:

 https://download.csdn.net/download/dalton2017/10475513