1. 程式人生 > >SpringMvc之Servlet 生命週期、工作原理-yellowcong

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>

請求時序圖

這裡寫圖片描述