標籤:servlet httpservlet 源碼解析servlet
在編寫 Servlet 時需要用到兩個用於所有 Servlet 的基本軟體包:javax.servlet 和 javax.servlet.http。
下面主要介紹 javax.servlet 提供的 Servlet 以及 javax.servlet.http 提供的 HttpServlet 應用編程介面。
Servlet源碼:
package javax.servlet;public interface Servlet{ public abstract void init(ServletConfig servletconfig) throws ServletException; public abstract ServletConfig getServletConfig(); public abstract void service(ServletRequest servletrequest, ServletResponse servletresponse) throws ServletException, IOException; public abstract String getServletInfo(); public abstract void destroy();}
以上便是Servlet的源碼,Servlet本身是一個介面,只是提供了幾個抽象方法。
init() 方法
在Servlet的生命週期中,僅執行一次 init() 方法,即在伺服器裝入Servlet時執行。通過設定管理員,可以設定在啟動伺服器或客戶機首次訪問Servlet 時裝入Servlet。無論有多少客戶機訪問Servlet,都不會重複執行 init()。
service() 方法
service()方法是Servlet的主體部分。客戶對一個HttpServlet對象的每次請求都會調用該對象的service() 方法,並傳遞給這個方法一個"請求"對象和一個"響應"對象作為參數。"請求"對象提供有關請求的資訊,而"響應"對象提供了一個將響應資訊返回給瀏覽器的通訊途徑。javax.servlet 軟體包中的相關類為ServletRequest和ServletResponse,而javax.servlet.http 軟體包中的相關類為HttpServletRequest 和 HttpServletResponse。Servlet 通過這些對象與伺服器通訊並最終與客戶機通訊。Servlet 能通過調用"請求"對象的方法獲知客戶機環境、伺服器環境的資訊和所有由客戶機提供的資訊;通過調用"響應"對象的方法,Servlet可以向客戶機發送響應。
HttpServlet中的service()方法預設的服務功能是調用與 HTTP 要求的方法相應的 do 功能。例如,如果 HTTP 要求方法為 GET,則預設情況下就調用 doGet()。當一個客戶通過HTML表單發出一個HTTP POST請求時,doPost()方法被調用。與POST請求相關的參數作為一個單獨的HTTP 要求從瀏覽器發送到伺服器。當需要修改伺服器端的資料時,應該使用doPost()方法。
Servlet的響應可以是下列幾種類型:
一個輸出資料流,瀏覽器根據它的內容類型(如text/HTML)進行解釋。
一個HTTP錯誤響應,重新導向到另一個URL、Servlet和JSP。
destroy() 方法
destroy() 方法僅執行一次,即在伺服器停止且卸載Servlet 時執行該方法。當伺服器卸載 Servlet 時,將在所有 service() 方法調用完成後,或在指定的時間間隔過後調用 destroy() 方法。一個Servlet 在運行service() 方法時可能會產生其他的線程,因此在調用 destroy() 方法時,必須確認這些線程已終止或完成。
GetServletConfig()方法
GetServletConfig()方法返回一個 ServletConfig 對象,該對象用來返回初始化參數和ServletContext。ServletContext 介面提供有關Servlet 的環境資訊。
GetServletInfo()方法
GetServletInfo()方法是一個可選的方法,它提供有關Servlet 的資訊,如作者、版本、著作權。
HttpServlet的源碼:
package javax.servlet.http;public abstract class HttpServlet extends GenericServlet implements Serializable{ ......此處省略N個字元}
從HttpServlet的源碼定義可以看到,HttpServlet與Servelt沒有關聯關係,而是繼承了GenericServlet,其實GenericServlet是一個抽象類別,該類已經實現了Servlet, ServletConfig, Serializable這三個介面。
在HttpServlet的源碼中可以看到,該類有兩個service()方法,下面我們一一說明:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response);}
通過代碼發現,這裡只是把相應的request和response轉換為了基於HTTP協議的相應對象。
Servlet介面中定義了一個service方法,HttpServlet對該方法進行了實現,將ServletRequest和ServletResponse轉換為HttpServletRequest和HttpServletResponse。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn‘t support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); }}
通過該service()方法可以發現,用戶端的請求先提交給Service方法時,先擷取提交的方法名,根據方法名調用相應的具體方法。如果重寫了該方法,那麼就不會根據方法名調用其他具體的方法了。
下面看一下HttpServlet類內部的幾個DoXXX()方法:
protected void doXXX(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_xxx_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(405, msg); } else { resp.sendError(400, msg); }}
發現doXXX()方法只是判斷協議類型,然後拋出相應的異常,其他什麼都沒做。所以,在實現自己的Servlet時,需要重寫這些方法,以符合自己的需求。一般不需要重寫service方法。
源碼解析Servlet和HttpServlet