[轉]通俗易懂Tomcat中Servlet的生命週期

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

其中因此引用的實例說明均來自本人本身的配置,如下全部例子均只實現了 doGet( ) 方法。apache

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

首先,咱們 在 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>
app

<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>
webapp

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

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

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

這個時候咱們會發如今 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  沒有被加載沒有關係,咱們在地址欄輸入 http://localhost:8080/accp/reqrep

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

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>

根據請求 http://localhost:8080/accp/reqrep     

找到 <servlet-name> reqrep 

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

這個時候咱們再作這樣的一個動做,把 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes 中的兩個類刪除掉,在 http://localhost:8080/accp/reqrep          或者  http://localhost:8080//accp/myservlet

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

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

1. Tomcat 加載 Servlet 而且實例化,有兩種方法:

一種是根據 web.xml中  <load-on-startup>0</load-on-startup>  的配置,在Tomcat 一啓動的時候就加載,並同時實例化,並立刻調用 其 init( ) 方法,完成初始化過程。

第二種就是動態的加載,根據客戶端的請求 URL 如: http://localhost:8080/accp/reqrep     中的accp/reqrep     

去在 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://blog.csdn.net/lvpin/archive/2007/06/09/1645770.aspx

 通俗易懂客戶端與服務器端交互原理(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 ,我正在找證據,已經發現一些端倪,今天有事暫時不寫。重點就是前面的內容了。

相關文章
相關標籤/搜索