1. 程式人生 > >[轉]通俗易懂Tomcat中Servlet的生命週期,講的非常詳細

[轉]通俗易懂Tomcat中Servlet的生命週期,講的非常詳細

我在上一篇文章裡詳細的介紹了 HTTP協議工作的流程,其中最重要的就是如何理解HTTP請求頭和HTTP響應頭,現在在這裡再來詳細的說明Tomcat 容器(即Servlet 容器)到底是如何 管理Servlet的,Servlet 的生命週期到底是如何進行的,其中與 Tomcat 容器的互動過程,相信大家只要看懂下面的分析,一定會真正理解Servlet 生命週期的。

其中所以引用的例項說明均來自本人自己的配置,以下所有例子均只實現了 doGet( ) 方法。

一. Tomcat 是如何 載入 Servlet 的,期間到底發生了什麼。(Servlet 的初始化)

首先,我們 在 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF 目錄下找到 web.xml 配置檔案,這個 配置檔案實際上起到的最直接的作用就是 管理 Servlet 。

裡面我先寫兩個 <servlet> 和<serlvet-mapping>

<servlet>
 <servlet-class>RequRepon</servlet-class>
 <servlet-name>reqrep</servlet-name>
</servlet>

<servlet-mapping>
 <servlet-name>reqrep</servlet-name>
 <url-pattern>/accp/reqrep</url-pattern>
</servlet-mapping>

<servlet>
 <load-on-startup>0</load-on-startup>                                                  -------------tomcat 服務一啟動就會載入
   <init-param>
  <param-name>num</param-name>
  <param-value>10000</param-value>
   </init-param>
       <servlet-name>www</servlet-name>
    <servlet-class>MyServlet</servlet-class>
   </servlet>

   <servlet-mapping>
       <servlet-name>www</servlet-name>
    <url-pattern>/accp/myservlet</url-pattern>
   </servlet-mapping>

在 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes  路徑下放好我已經經過編譯後的兩個 Servlet  .class 檔案 ,一個是 MyServlet,一個是 RequRepon

啟動 C:\apache-tomcat-5.5.20\bin 路徑下 startup.bat ,tomcat 開始啟動。

這個時候我們會發現在 Tomcat 命令控制檯上出現了 一句話 ,

MyServlet 中的 init () 方法被呼叫了一次!

其實我在 MyServlet ,和 RequRepon 都寫了 ---####的 init () 方法被呼叫了一次!但是RequRepon 中的init()並沒有被呼叫,原因就在於 web.xml 中

<servlet>
 <load-on-startup>0</load-on-startup>                                                  -------------tomcat 服務一啟動就會載入
   <init-param>
  <param-name>num</param-name>
  <param-value>10000</param-value>
   </init-param>
       <servlet-name>www</servlet-name>
    <servlet-class>MyServlet</servlet-class>
   </servlet>

就通知了 Tomcat 在它的服務一啟動的時候 ,就要去載入 所對應的 Servlet-class   MyServlet 這個類,0代表優先級別,隨後是 1. 2. 3. .4.... 0的優先級別最高。Tomcat 首先把這些類載入並例項化,儲存在自己的Servlet 容器池中,以後如果有請求,直接從此容器池中取出來,處理相應的請求。

這個時候出現:注意最下面的兩行

RequRepon 中的 init( ) 方法不僅被呼叫了,並且其 doGet( ) 方法也被呼叫了!

那麼這是為什麼呢?原因就是雖然 RequRepon  首先被載入到 Tomcat 容器中,但是一旦有來自客戶端的請求,Tomcat 會解析這個請求 URL 找到指定的 Servlet 類,再載入同時處理請求,所以就會呼叫它的 doGet( )方法

<servlet>
 <servlet-class>RequRepon</servlet-class>
 <servlet-name>reqrep</servlet-name>
</servlet>

<servlet-mapping>
 <servlet-name>reqrep</servlet-name>
 <url-pattern>/accp/reqrep</url-pattern>                            

</servlet-mapping>

找到 <servlet-name> reqrep 

根據 reqrep   找到對應的 <servlet-class> RequRepon  載入並初始化

會發現 Tomcat 中 這兩個類的 doGet( ) 方法仍然被實現了,也就說來自於客戶端的請求他們已經收到了,並呼叫了doGet( ) 方法進行了處理。

再來總結以上的內容,就是Servlet 如何被載入的。

1. Tomcat 載入 Servlet 並且例項化,有兩種方法:

一種是根據 web.xml中  <load-on-startup>0</load-on-startup>的配置,在Tomcat 一啟動的時候就載入,並同時例項化,並馬上呼叫 其 init( ) 方法,完成初始化過程。

去在 web.xml 中尋找指定的 Servlet 類,載入它並例項化,馬上呼叫其 init( ) 方法完成初始化,由於是來自客戶端的一個請求,那麼自然要 呼叫 doGet( ) 方法來處理請求。

2. Servlet 的初始化 ,即init( ) 方法是優於其它所有方法之前的,在處理任何響應之前就會呼叫的,並且在一個生命週期中有且只有一次!

3. Tomcat 一旦載入 Servlet ,例項化,就將其儲存在記憶體中,以後不管多少次來自於客戶端的請求都是由儲存在Tomcat 容器池(即記憶體)裡面的 Servlet例項來處理,它不是開啟了一個程序,而是實現了多執行緒操作,這也就是為什麼要引用 Servlet 而不用 CGI 的優點。這個證據就是 我已經在C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes  路徑下刪除了 這兩個Servlet  .class 檔案,但是請求一樣處理,這就能證明,Tomcat 完成類載入是將他們儲存在記憶體中,以便以後使用,效率很高。

二.doGet( ) 和 doPost( ) 到底是怎麼樣來的,為什麼沒有 Service( ) 方法,裡面的流程是什麼?

上面只說明瞭 init( ) 有關的東西,現在來說 doGet( ) 和 doPost( ) ,那麼這個要和我的另一篇文章 關於 HTTP 請求頭和HTTP 響應頭結合起來看,會有很好的效果的。

 通俗易懂客戶端與伺服器端互動原理(HTTP資料請求與HTTP響應,包括Servlet部分)

當Tomcat 容器收到一個訊息,使用者請求一個Servlet ,那麼就生成了一個響應頭,其實就是 HttpServletRequest 物件,根據 url 指定的 Servlet ,Tomcat 就讓此 Servlet 來處理這個請求。

那麼我們在 Java程式中其實實現的就是 HttpServelt ,重寫了 doGet( )或者doPost( ) 方法,就可以處理這個請求了。但是 doGet( ) 或者 doPost( ) 方法是由 Service( )來呼叫的,我們怎麼不重寫Service( ) 方法呢?那是因為HttpServlet 繼承了GenericServlet,而GenericServlet 實現了Servlet 介面。在其中的一個類中,它的 Service( ) 其實就已經解析了 來自於 Http請求頭的內容,根據請求行的 method  來決定呼叫 doGet( ) 或者 doPost( ) 方法,同時把HttpServletRequest 和 HttpServletResponse 以引數的形式傳遞給 doGet( ) 或者doPost(),來進行我們的業務處理。

如果重寫 Service( )方法,那麼裡面的很多工作可能就需要我們完成,其實這並不需要,我們只需要重寫 doGet( ) 或者 doPost(  )方法就可以了。這個重寫的動作就是Servlet 生命週期中的 Service( ) 方法,裡面呼叫了 doGet( ) 或者doPost( )方法。

三.處理請求結束,生成響應回發,銷燬例項

處理請求結束,生成響應回發,這個在我上面所提到的文章裡已經講的很清楚了。

至於銷燬例項 ,完成 destroy( ) 動作,一是隻要把 tomcat服務關閉就可以了。另外還有一種能實現 destroy( ) 的方法,可能是 Tomcat 裡面的一些小BUG,我正在找證據,已經發現一些端倪,今天有事暫時不寫。重點就是前面的內容了。