| 2.4 Servlet的生命周期((life cycle) -------------------------------------------------------------------------------- 到目前為止,您已經(jīng)學(xué)習(xí)到了如何撰寫、編譯與執(zhí)行Servlet。由于所有的Servlet都必須執(zhí)行于Servlet容器中,因此我們必須了解Servlet容器如何進行下列工作: n 何時載入Servlet,并予以實體化(instantiated) n 如何回應(yīng)用戶的請求 n 何時該卸載Servlet的服務(wù) 上述三個階段即構(gòu)成Servlet的“生命周期”(life cycle)。值得注意的是,Servlet生命周期并不由程序員控制,而是由Servlet容器掌管。 在javax.servlet.Servlet接口內(nèi)定義了下面三個方法: n init() n service() n destroy() 這意味著所有的Servlet都會實現(xiàn)這三個方法,而Servlet容器可由此控制其生命周期。本節(jié)將深入探討這些方法的用途及使用時機。 2.4.1 service()方法 在某些情況下,Servlet容器需要將Servlet實體化(例如:Servlet容器接收到某個HTTP請求)。簡單地說,實體化的過程就是建立一個新的Servlet,可簡單地概分為下列兩個階段。 (1) 在實體化Servlet以前,Servlet容器必須確認(rèn)與Servlet有關(guān)的類位于何處,再以“類載入器”(ClassLoader)將Servlet類載入存儲器。 (2) 當(dāng)Servlet被調(diào)用時,Servlet容器將會調(diào)用該Servlet的默認(rèn)構(gòu)造函數(shù)(default constructor),然后建立一個Servlet實體。 因為Servlet的構(gòu)造函數(shù)是由Servlet容器調(diào)用的,所以Servlet類不應(yīng)該包含“非默認(rèn)構(gòu)造函數(shù)”(non-default constructor)。所謂的“非默認(rèn)構(gòu)造函數(shù)”是指可以傳遞參數(shù)的構(gòu)造函數(shù)。此時您可能產(chǎn)生一個疑問:如果Servlet沒有“非默認(rèn)構(gòu)造函數(shù)”,如何將起始參數(shù)傳遞給Servlet呢?這問題問得好,在回答前,筆者先說明何時需要將起始參數(shù)傳遞給Servlet。 試想下列情境:某個Servlet需要通過JDBC存取數(shù)據(jù)庫。依照標(biāo)準(zhǔn)程序,您必須指定JDBC驅(qū)動程序的種類、目的數(shù)據(jù)庫位置以及登錄數(shù)據(jù)庫所使用的賬號與密碼,然后利用這些信息建立與數(shù)據(jù)庫的連接。然而將這些信息全部撰寫在每個Servlet里面將造成一個潛在問題:萬一后端數(shù)據(jù)庫需要更換,所有Servlet程序碼豈不是要全部改寫!由此可見,比較妥善的方式應(yīng)該是將所有Servlet都會存取的數(shù)據(jù)統(tǒng)一存放在某個位置,以便后續(xù)管理與維護。 在Java Servlet技術(shù)所建構(gòu)的Web應(yīng)用程序中,每個Web應(yīng)用程序均可定義一個部署描述文件:web.xml。該文件可存放Servlet所需要的起始參數(shù)(例如剛才提及的數(shù)據(jù)庫目的位置與用戶密碼)以及Web應(yīng)用程序的結(jié)構(gòu)數(shù)據(jù)。當(dāng)Servlet容器讀取web.xml文件內(nèi)容后,可以將這些起始參數(shù)封裝成一個對象,并在調(diào)用init()方法時傳遞給Servlet,這個做法即可達(dá)到初始化Servlet起始參數(shù)的目的。 以下是javax.servlet.Servlet類內(nèi)定義的init()方法: public void init(ServletConfig config) throws ServletException{ } 由此可知:Servlet容器調(diào)用init()方法時,將會傳入一個ServletConfig對象。因此我們可以在Servlet內(nèi)覆寫init()方法,并通過ServletConfig對象來取得某些起始參數(shù)。關(guān)于如何定義與存取Servlet起始參數(shù),將留待第3章再做說明。 n 如何覆寫init()方法 如果您仔細(xì)研究了Servlet API的定義,也許會發(fā)現(xiàn)一個奇怪的現(xiàn)象:javax.servlet接口只定義了一個init()方法,但是javax.servlet.GenericServlet類(繼承自javax.servlet)內(nèi)卻定義了兩個init()方法(參考圖2-13),這個問題倒是值得我們研究。 由于javax.servlet接口內(nèi)定義的init(ServletConfig config)方法必須傳入一個ServletConfig對象,因此在覆寫該方法時,必須再以super.init(config) 調(diào)用父類的init()方法,這個步驟稍嫌繁瑣。為了簡化init()方法的覆寫方式,javax.servlet.GenericServlet類另外定義了一個不需傳遞參數(shù)的init方法,以便程序員直接覆寫init()方法。當(dāng)Servlet容器調(diào)用init(ServletConfig config)方法時,該方法也會自動調(diào)用您所覆寫的init()方法。 提示: 在GenericServlet類內(nèi)定義的init()方法并未傳入ServletConfig對象。如果在此方法內(nèi)須要存取ServletConfig對象,可直接調(diào)用getServletConfig()方法。 圖2-13 GenericaServlet類與Servlet接口 2.4.2 service()方法 當(dāng)Servlet實體經(jīng)過初始化程序后,只要Servlet 容器接收到用戶傳送的請求,就會將這個請求封裝成ServletRequest對象,然后調(diào)用service()方法。以下是javax.servlet.Servlet接口所定義的service()方法。 public void service(ServletRequest req, ServletResponse res) throws ServletException,java.io.IOException 如果用戶傳送的是HTTP請求,Servlet容器就會將HTTP請求封裝成HttpServletRequest對象,然后調(diào)用service()方法。以下是在javax.servlet.http.HttpServlet類中定義的service()方法: protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException,java.io.IOException 針對HTTP請求,service()方法會根據(jù)HTTP請求形式(GET、POST、HEAD等)來決定應(yīng)該執(zhí)行哪種doXXX()方法。如前所述,如果HTTP請求形式為POST,service()方法將會自動調(diào)用doPost()方法。因此在一般情況下,程序員無須去覆寫service()方法,只須根據(jù)用戶可能傳送的HTTP形式去覆寫doGet()與doPost()等方法即可。 |
|
|