SpringMvc之Servlet 生命週期、工作原理-yellowcong
我最開始寫的是SpringMVC的開始案例,從SpringMvc的請求原理,寫著寫著,就寫到了Servlet的生命週期了,感覺越寫越底層啊。主要掌握Servlet的生命週期,以及HttpServler與Servlet的關係
類關係結構
Servlet
Servlet這個類是一個介面,並不是一個實體類,但是這個介面可以說,就是祖師爺了,所有的Servlet服務都實現了它。
servlet的生命週期,就是init ->service ->destroy,可以說就是挺簡單的,在JDK中的,Servlet的實現類是GenericServlet
GenericServlet
GenericServlet 這個類是一個抽象類,並沒有完全的實現Servlet的所有介面,service 這個方法為抽象方法,由子類去實現(這種設計模式被成為模板模式(鉤子函式))
service方法是抽象方法,大家可以仔細的看我框出來的那個函式,前面有一個 A
,表示的是抽象方法
HttpServlet
HttpServlet實現了GenericServlet類,但是仍然是抽象類,需要子類去繼承HttpServlet,然後複寫裡面的doGet和doPost方法
下圖的程式碼可以看到,雖然是實現了Servlet所有介面,但是並不能直接使用,需要子類去繼承HttpServlet
service方法與doGet和doPost關係
類/介面 | 關係 |
---|---|
javax.servlet.Servlet(介面) | 定義的介面 |
javax.servlet.GenericServlet(抽象類) | 實現Servlet |
javax.servlet.http.HttpServlet(抽象類) | 繼承了GenericServlet抽象類 |
在下程式碼,大家可以發現,所謂的doGet和doPost方法,都不過是一個鉤子函式,大家最後呼叫的都是service方法,在方法裡面判斷請求的型別,然後再去呼叫鉤子函式doGet/doPost
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
//請求的方法是否是get型別
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);
//是否是post型別
} 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);
}
}
ServletRequest/HttpServletRequest
在HttpServlet中的service方法,會將介面定義的ServletRequest 轉化為HttpServletRequest,然後呼叫自己複寫的service方法來處理請求。
大家看圖會發現,其實HttpServletRequest介面繼承了ServletRequest介面和HttpServletResponse介面繼承了ServletResponse介面
說實話有人真的很無尿,會問你,HttpServletRequest和ServletRequest的區別,我尼瑪想呼死你,這兩個介面的關係是繼承關係,區別就是HttpServletRequest(子類)擁有更多的http相關處理的方法咯
生命週期
Servlet 生命週期:Servlet 載入—>例項化—>服務—>銷燬。
方法 | 執行次數 | 執行時候 |
---|---|---|
init | 一次 | 1、客戶端呼叫Servlet,2、容器初始化,在web.xml配置load-on-startup,3、容器初始化,配置了init-param |
service | 多次 | 每次客戶端訪問servlet,都會呼叫 |
destroy | 一次 | 當容器退出時(Tomcat退出) |
init(只執行一次,在):在Servlet的生命週期中,僅執行一次init()方法。它是在伺服器裝入Servlet時執行的,負責初始化Servlet物件。可以配置伺服器,以在啟動伺服器或客戶機首次訪問Servlet時裝入Servlet。無論有多少客戶機訪問Servlet,都不會重複執行init。
service(執行多次):它是Servlet的核心,負責響應客戶的請求。每當一個客戶請求一個HttpServlet物件,該物件的Service()方法就要呼叫,而且傳遞給這個方法一個“請求”(ServletRequest)物件和一個“響應”(ServletResponse)物件作為引數。在HttpServlet中已存在Service()方法。預設的服務功能是呼叫與HTTP請求的方法相應的然後去呼叫doGet和doPost等。
destroy(執行一次,在容器退出(Tomcat關閉)): 僅執行一次,在伺服器端停止且解除安裝Servlet時執行該方法。當Servlet物件退出生命週期時,負責釋放佔用的資源。一個Servlet在執行service()方法時可能會產生其他的執行緒,因此需要確認在呼叫destroy()方法時,這些執行緒已經終止或完成。
建立Servlet物件的時機
1、Servlet容器啟動時:讀取web.xml配置檔案中的資訊,構造指定的Servlet物件,建立ServletConfig物件,同時將ServletConfig物件作為引數來呼叫Servlet物件的init方法。
2、在Servlet容器啟動後:客戶首次向Servlet發出請求,Servlet容器會判斷記憶體中是否存在指定的Servlet物件,如果沒有則建立它,然後根據客戶的請求建立HttpRequest、HttpResponse物件,從而呼叫Servlet 物件的service方法。
3、Servlet Servlet容器在啟動時自動建立Servlet,這是由在web.xml檔案中為Servlet設定的屬性決定的。從中我們也能看到同一個型別的Servlet物件在Servlet容器中以單例的形式存在。
<servlet>
<servlet-name>Init</servlet-name>
<servlet-class>com.yellowcong.servlet.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>