1. 程式人生 > >servlet api簡介(一)

servlet api簡介(一)

javax.servlet包中有9個類,如下,其中最後2個是異常類:

  • GenericServlet
  • ServletContextEvent
  • ServletContextAttributeEvent
  • ServletInputStream
  • ServletOutputStream
  • ServletRequestWrapper
  • ServletResponseWrapper
  • ServletException
  • UnavailableException

GenericServlet抽象類可以用於開發獨立於協議的servlet,為此我們只需要實現service方法。然後日常程式設計中,更常見的是使用HttpServlet。

ServletContextEvent和ServletContextAttributeEvent是用於提供ServletContext及其屬性的改變情況的事件類。

ServletInputStream和ServletOutputStream為與客戶端通訊,即傳送和讀取二進位制資料提供了輸入、輸出流。

包裝器類ServletRequestWrapper、ServletResponseWrapper提供了ServletRequest和ServletResponse介面的有用實現。我們可以從它們派生出子類,以增強包裝物件的功能。

ServletException是servlet在遇到問題必須放棄時可以丟擲的一個通用異常,丟擲這個異常表明使用者請求、處理請求或者傳送響應時出了問題。這個異常拋到了servlet容器中以後,應用程式便失去了處理請求的控制權,容器會接著負責清理這些請求,並向客戶返回一個響應。根據容器的實現和配置,容器可能向用戶返回一個出錯頁面。

當一個過濾器或servlet臨時或永久性地不可用時,應該丟擲UnavailableException,可以應用在servlet請求的資源(資料庫、DNS、其他servlet等等)不可用時。

Servlet介面

生命週期方法

所有的servlet必須實現Servlet介面。該介面定義了三個生命週期方法,由servlet容器呼叫:

  • public void init(ServletConfig config) throws ServletException
  • public void service(ServletRequest  req, ServletResponse res) throws ServletException, IOException
  • public void destroy()

servlet生命週期有著明確定義。客戶向Web伺服器發出一個請求,伺服器再根據需要把請求重定向到servlet容器:

  1. servlet的載入和例項化有servlet容器負責。容器需要找到servlet類,載入並例項化,以供使用。
  2. 容器通過呼叫init方法來初始化servlet,傳入init的引數是一個ServletConfig物件,描述了該servlet的執行環境。
  3. 若servlet沒有成功初始化,則丟擲UnavailableException或ServletException異常,該servlet會被釋放,然後系統會試圖例項並初始化一個新的servlet。
  4. 若servlet成功初始化,則表明servlet已經準備完畢,可以處理客戶請求。請求和響應資訊分別包裝在ServletRequest、ServletResponse物件中,它們被傳遞到service方法中,由service方法負責處理請求並且返回響應資訊。
  5. ServletException、UnavailableException異常都可以在請求處理時丟擲。若丟擲一個異常,則容器必須清理掉該請求,可能要解除安裝相應的例項並呼叫destroy方法。
  6. 一旦容器決定刪除某個servlet,則必須允許任何service呼叫結束。之後再呼叫destroy方法,進而釋放相應例項以便垃圾回收。

其他方法

在Servlet介面中還有更多的方法需要由servlet實現:

  • public ServletConfig getSerletConfig(),獲取初始化引數。該函式返回的ServletConfig例項中,包含了servlet的初始化和啟動引數,這個例項在servlet初始化過程中被傳遞給init。
  • public String getServletInfo() throws ServletException, IOException,獲取關於servlet的資訊。該方法返回String物件,包含了關於該servlet的資訊,如作者、版本、版權等資訊,具體返回哪些內容由開發者決定(預設是空串),Web伺服器管理工具可以呼叫該方法顯示servlet的資訊。

servlet執行緒問題

伺服器/容器會收到很多請求,這些經常會同時出現,因此容器必須負責為每個請求建立不同的執行緒。service方法可以由容器在不同執行緒中呼叫,來處理各個不同的請求。由於容器是在不同執行緒中處理請求,所以servlet必須編寫為執行緒安全(thread-safe)程式碼。為了實現執行緒安全,我們需要進行適當的管理,如同步鎖等;此外,servlet提供了SingleThreadModel介面,但這種實踐將導致 Web 容器建立多個 servlet 例項,即為每個使用者建立一個例項。對於任何大小的應用程式,這種實踐都將導致嚴重的效能問題【參考2】。 

抽象類GenericServlet

抽象類GenericServlet實現了三個介面,Servlet、ServletConfig、java.io.Serializable【參考3】,對於Servlet介面而言,除了service()方法外,它提供了其他所有內容的具體實現,所以當我們要繼承GenericServlet時,必須至少要實現service()方法。也就是說,大多數標準工作可以交給從父類GenericServlet繼承而來的方法;如果真的有必要更改預設實現,我們只需重寫(override)相應方法。注意,除了Servlet介面中定義的方法外,GenericServlet還定義了其他幾個方法。

生命週期方法

  • 初始化方法:public void init(ServletConfig config)、public void init()。有引數的init方法是Servlet介面中方法的實現。當容器呼叫時,init(ServletConfig)會在servlet中儲存ServletConfig物件的一個引用,然後呼叫無參的init方法。這個無參的init方法是為了方便使用而提供的,省卻了 super.init(config) 的呼叫。
  • public abstract void service(ServletRequest req, ServletResponse res)。這裡的service方法是抽象方法,子類必須實現該方法以處理它們的請求。
  • public void destroy()。destroy方法由容器呼叫,以保證沒有資料損失、相關資源得到正確釋放。

servlet環境方法

除了getServletConfig、getServletInfo,由於實現了ServletConfig介面,GenericServlet還提供了其他的、繼承自ServletConfig介面的、環境有關的方法【參考3】。

  • public ServletContext getServletContext(),可以得到servlet容器的相關資訊。
  • public Enumeration getInitParameterNames(),返回servlet初始化引數名字的列舉集合。
  • public String getInitParameter(String name),結合上面的函式,來獲取引數的值。
  • public String getServletName(),每個servlet都是通過名字由servlet容器識別的,而通過該方法即可獲得該servlet例項的名字。

實用方法

GenericServlet提供了兩個日誌記錄方法,這樣servlet就可以向web應用程式的日誌檔案寫入資訊:

  • public void log(String msg)
  • public void log(String msg, java.lang.Throwable t)

ServletRequest介面

繼承自它的介面有HttpServletRequest,繼承自它的類有HttpServletRequestWrapper、ServletRequestWrapper。ServletRequest介面負責包裝客戶請求,並提供方法使請求資訊可供servlet使用,下面按功能對介面的函式進行介紹。

獲取請求引數

  • public java.util.Enumeration getParameterNames(),返回該請求中所有引數名字構成的列舉
  • public String getParameter(String name),返回指定引數的值
  • public String[] getParameterValues(String name),與上面方法相似,在引數對應的是多個值(陣列形式)的情況下,使用這個方法
  • public java.util.Map getParameterMap(),返回一個Map物件,包含了所有請求引數,其中引數名字作為鍵,引數值作為值。

訪問請求頭資訊

  • public int getContentLength(),獲取請求的大小(在附帶一個檔案或者其他大型物件時使用),返回以位元組為單位的長度值(如果長度未知則返回-1)
  • public String getContentType(),獲取請求實體中的資料型別,返回請求的MIME型別(如果未知則返回null)
  • public String getProtocol(),返回發出請求時所使用的協議名字和版本號,如HTTP/1.1

使用屬性

屬性類似於請求引數,但不是由客戶設定,而是由servlet容器設定,或者由此前使用javax.servlet.RequestDispatcher把請求和附加資訊作為屬性轉發給請求的servlet設定。同時,屬性值可以是任何Java物件。

  • public java.util.Enumeration getAttributeNames(),返回該請求的屬性名組成的Enumeration物件
  • public Object getAttribute(String name),返回指定的屬性值(返回的是Object型別,可能需要轉型~)
  • public void setAttribute(String name,Object o),儲存指定的屬性值,一般用在把請求轉發到servlet或過濾器進行處理的時候
  • public void removeAttribute(String name),從請求中刪除指定屬性,一般用在把請求轉發到servlet或過濾器進行處理的時候

獲取請求路徑資訊

  • public String getScheme(),返回建立請求的協議,例如http、https或ftp。不同的協議會有不同的構造URL的規則,servlet需要知道相應的方案才能夠正確地解釋URL。
  • public String getServerName(),返回接收這個請求的伺服器名字,也是客戶定址伺服器使用的名字。若伺服器通過IP地址而不是名字來定址,則這個方法返回IP地址。
  • public int getServerPort(),返回伺服器接收其請求時使用的埠號。不同的協議使用不同的預設埠,伺服器可以在任何空閒的合法埠上偵聽請求。

檢查安全連線

public boolean isSecure(),servlet可通過該方法確定當前處理的請求是否在一個安全的連線上。這個方法尤其適合於過濾器、閘道器servlet,它們可能被設定為截獲請求並且重定向到需要安全連線的資源上。傳輸敏感資訊,如信用卡、密碼等資料時需要用到。

國際化處理

有些方法可以幫助我們開發適應於世界不同地區和字符集的程式。客戶所傳送的請求中包含了相關資訊,這些資訊表明它願意使用什麼地區設定來接收響應。提供一些列的地區是很有必要的,若servlet不支援首選設定,可能會支援其次的設定,如此儘可能地滿足使用者要求。如果客戶沒有指定地區設定,那麼這些方法返回伺服器的預設地區設定。

  • public java.util.Locale getLocale(),返回首選地區設定
  • public java.util.Enumeration getLocales(),返回一組地區,這些按照優先性由高到低排列

編碼問題也是處理請求中需要考慮的:

  • public String getCharacterEncoding(),返回在請求中使用的字元編碼名字(若沒有指定則返回null)
  • public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException,替代在請求中使用的字元編碼,必須在使用getReader或getInputStream方法讀取請求引數或者讀取輸入之前呼叫它。預設的編碼方式是ISO-8859-1。

從請求中讀取流

Servlet API為請求輸入流和響應輸出流提供了兩個IO流包裝類。
  • public ServletInputStream getInputStream() throws java.io.IOException,使用這個方法從請求中讀入一個檔案或序列化物件,可把返回的物件包裝在有效的輸入類中,如ObjectInputStream,以讀入資料
  • public java.io.BufferedReader getReader() throws java.io.IOException,使用這個方法讀入字元資料,可把返回的物件包裝在一個適當的輸入類中,如FileReader,來讀取資料

注意,我們只能呼叫兩者中的一個。如果呼叫了一個後,再嘗試呼叫另一個,則會丟擲IllegalStateException異常,表明已經呼叫了其他方法讀取資料。

獲取客戶資訊

  • public java.lang.String getRemoteAddr(),返回客戶的IP地址
  • public java.lang.String getRemoteHost(),返回客戶的完整名字

生成RequestDispatcher

servlet通過使用RequestDispatcher物件,把請求轉發給指定資源或者在響應中包含該資源。這個資源可以是動態或靜態的。
  • public RequestDispatcher getRequestDispatcher(String path),返回RequestDispatcher物件,該物件包裝這path引數中指定的資源,該資源可以是另一個servlet或者靜態資源(如HTML檔案)

ServletResponse介面

ServletResponse物件用於把servlet的響應回送給客戶。它把相應資訊包裝在一個介面中【參考4】。

設定內容長度和MIME型別

  • public void setContentLength(int len),設定響應頭,用於指定響應資訊的長度
  • public void setContentType(java.lang.String type),用於設定MIME型別,這個方法應該在輸出流物件寫任何輸出資訊之前呼叫。對於標準的網頁,使用“text/html”,對於序列化的Java物件,使用“application/x-java-serialized-object”

國際化處理

針對特殊使用者定製響應資訊,包括地區設定和字符集設定。

  • public java.util.Locale getLocale(),訪問地區資訊
  • public void setLocale(java.util.Locale loc),重新設定響應資訊的地區資訊,必須在getWriter之前呼叫
  • public java.lang.String getCharacterEncoding(),返回響應資訊中MIME實體使用的字符集型別,預設是ISO-8859-1
  • public void setCharacterEncoding(java.lang.String charset),設定MIME的字符集型別

在響應中返回資料

為了在響應中返回資訊,需要從Response物件中訪問輸出物件,可以通過呼叫下列方法之一來實現:

  • public ServletOutputStream getOutputStream() throws java.io.IOException,返回的ServletOutputStream物件直接用於或包裝在一個IO物件中,以輸出一個響應。
  • public java.io.PrintWriter getWriter() throws java.io.IOException,返回PrintWriter物件,向客戶回送文字資訊。

注意,只能呼叫上述中的一個方法。因此需要決定將向客戶回送什麼型別的資料,對於字元資料要使用getWriter,對於混合型別(如序列化物件、檔案等),要使用getOutputStream並且把返回的物件包裝在適當類中以完成資料回送。

輸出緩衝儲存

不同servlet容器的緩衝儲存策略各不相同,容器對此並沒有做什麼要求。有許多方法可供servlet使用以改善輸出緩衝儲存策略。

  • public int getBufferSize(),返回使用的基層緩衝儲存區的大小
  • public void setBufferSize(int size),允許servlet向容器建議一個緩衝區大小,容器要實現的緩衝區大小至少為這個值,必須在向客戶返回任何內容之前呼叫該方法
  • public boolean isCommitted(),可以供servlet確定響應資訊是否已經開始開始被傳送到客戶
  • public void reset(),如果響應資訊還沒有被提交,reset方法將清除緩衝區中的所有資料(包括資料、響應頭、狀態碼) ;如果響應資訊已經被提交,將丟擲IllegalStateException異常
  • public void resetBuffer(),與reset類似,resetBuffer講復位緩衝區中的資料(但不包括響應頭、狀態碼),且若響應資訊已提交,將丟擲IllegalStateException異常
  • public void flushBuffer() throws java.io.IOException,立即把緩衝區中的內容傳送給客戶。一旦一個緩衝區滿了,容器會立即把它的內容傳送給客戶,也就提交了響應資訊。

每個容器請求都在改變緩衝區大小,會產生一種累積效果。對於傳入的新的值,容器會決定保持較大的緩衝區大小。如果把緩衝區增加的太大(快丟擲異常了),就必須把緩衝區大小重新設定為一個有意義的值,如1024(通過用新值重新編譯,或者重新啟動伺服器),然後才能繼續使用這個servlet。

參考

1 《Java Servlet程式設計指南》

2 http://www.ibm.com/developerworks/cn/websphere/library/bestpractices/do_not_implement_single_thread_model_in_servlets.html

3 http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/GenericServlet.html

4 http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletResponse.html