Servlet 是一種比JSP 更早的動(dòng)態(tài)網(wǎng)頁編程技術(shù)。在沒有JSP 之前, Servlet 也是同時(shí)充當(dāng)視圖層、業(yè)務(wù)邏輯層及持久層角色。
Servlet 的開發(fā)效率非常低,特別是當(dāng)使用Servlet 生成表現(xiàn)層頁面時(shí),頁面中所有的HTML 標(biāo)簽,都需采用Servlet 的輸出流來輸出,因此極其煩瑣。由于Servlet 是個(gè)標(biāo)準(zhǔn)的Java 類,因此必須由程序員開發(fā),其修改難度大,美工人員根本無法參與Servlet 頁面的開發(fā)。這一系列的問題,都阻礙了Servlet 作為表現(xiàn)層的使用。
自MVC 規(guī)范出現(xiàn)后, Servlet 的責(zé)任開始明確下來,僅僅作為控制器使用,不再需要生成頁面標(biāo)簽,也不再作為視圖層角色使用。
Servlet ,通常稱為服務(wù)器端小程序,是運(yùn)行在服務(wù)器端的程序,用于處理及響應(yīng)客戶端請求。
Servlet 是個(gè)特殊的Java 類,這個(gè)Java 類必須繼承HttpServlet 。每個(gè)Servlet 可以響應(yīng)戶端的請求。Servlet 提供不同的方法用于響應(yīng)客戶端請求。
doGet: 用于響應(yīng)客戶端的get 請求。
doPost: 用于響應(yīng)客戶端的post 請求。
doPut: 用于響應(yīng)客戶端的put 請求。
doDelete: 用于響應(yīng)客戶端的delete 請求。
事實(shí)上,客戶端的請求通常只有g(shù)et 和post 兩種; Servlet 為了響應(yīng)這兩種請求,必須重寫doGet 和doPost 兩個(gè)方法。如果Servlet 為了響應(yīng)四個(gè)方法,則需要同時(shí)重寫上面的四個(gè)方法。
大部分時(shí)候, Servlet 對于所有請求的響應(yīng)都是完全一樣的。此時(shí),可以采用重寫一個(gè)方法來代替上面的幾個(gè)方法, Servlet 只需重寫service 方法即可響應(yīng)客戶端的所有請求。另外, HttpServlet 還包含兩個(gè)方法。
init(ServletConfig config): 創(chuàng)建Servlet 實(shí)例時(shí),調(diào)用的初始化方法。
destroy: 銷毀Servlet 實(shí)例時(shí),自動(dòng)調(diào)用的資源回收方法。
通常無須重寫init和destroy兩個(gè)方法,除非需要在初始化Servlet 時(shí),完成某些資源初始化的方法,才考慮重寫init 方法。如果需要在銷毀Servlet 之前,先完成某些資源的回收,比如關(guān)閉數(shù)據(jù)庫連接等,才需要重寫destroy 方法。
注意:如果重寫了init(ServletConfig config)方法,則應(yīng)在重寫該方法的第一行調(diào)用super.init(config) 。該方法將調(diào)用HttpServlet 的init 方法。
Servlet 和JSP 的區(qū)別在于:
Servlet 中沒有內(nèi)置對象,原來JSP 中的內(nèi)置對象都必須通過HttpServletRequest對象,或由HttpServletResponse 對象生成:
對于靜態(tài)的HTML 標(biāo)簽, Servlet 都必須使用頁面輸出流逐行輸出。
這也正是筆者在前面介紹的: JSP 是Servlet 的一種簡化,使用JSP 只需要完成程序員需要輸出到客戶端的內(nèi)容,至于JSP 中的Java 腳本如何鑲嵌到一個(gè)類中,由JSP 容器完成。而Servlet 則是個(gè)完整的Java 類,這個(gè)類的service 方法用于生成對客戶端的響應(yīng)。
Servlet 的配置
編輯好的Servlet 源文件并不能響應(yīng)用戶請求,還必須將其編譯成class 文件。將編譯后的HelloServlet. class 文件放在WEB-INF/classes 路徑下,如果Servlet 有包,則還應(yīng)該將class 文件放在對應(yīng)的包路徑下。
為了讓Servlet 能響應(yīng)用戶請求,還必須將Servlet 配置在Web 應(yīng)用中。配置Servlet時(shí),需要修改web.xrnl 文件。
配置Servlet 需要配置兩個(gè)部分。<servlet>/<servlet-mapping>
Servlet的生命周期
Servlet 在容器中運(yùn)行,其實(shí)例的創(chuàng)建及銷毀等都不是由程序員決定的,而是由容器進(jìn)行控制。
Servlet 的創(chuàng)建有兩個(gè)選擇。
客戶端請求對應(yīng)的Servlet 時(shí),創(chuàng)建Servlet 實(shí)例:大部分的Servlet 都是這種Servlet。 Web 應(yīng)用啟動(dòng)時(shí),立即創(chuàng)建Servlet 實(shí)例:即load-on-startup Servlet。
每個(gè)Servlet 的運(yùn)行都遵循如下生命周期。
(1)創(chuàng)建Servlet 實(shí)例。
(2) Web 容器調(diào)用Servlet 的init 方法,對Servlet 進(jìn)行初始化。
(3) Servlet 初始化后,將一直存在于容器中,用于響應(yīng)客戶端請求。如果客戶端有g(shù)et 請求,容器調(diào)用Servlet 的doGet 方法處理并響應(yīng)請求。對于不同的請求,有不同的處理方法,或者統(tǒng)一使用service 方法處理來響應(yīng)用戶請求。
(4) Web 容器角色銷毀Servlet 時(shí),調(diào)用Servlet 的destroy 方法,通常在關(guān)閉Web容器之時(shí)銷毀Servlet。
使用Servlet創(chuàng)作為控制器
正如前面見到,使用Servlet 作為表現(xiàn)層的工作量太大,所有的HTML 標(biāo)簽都需要使用頁面輸出流生成。因此,使用Servlet 作為表現(xiàn)層有如下三個(gè)劣勢。
開發(fā)效率低,所有的HTML 標(biāo)簽都需使用頁面輸出流完成。
不利于團(tuán)隊(duì)協(xié)作開發(fā),美工人員無法參與Servlet 界面的開發(fā)。
程序可維護(hù)性差,即使修改一個(gè)按鈕的標(biāo)題,都必須重新編輯Java 代碼,并重新編譯。
整個(gè)結(jié)構(gòu)非常清晰,下面是MVC 中各個(gè)角色的對應(yīng)組件。
M: Model,即模型,對應(yīng)JavaBean 。
V: View ,即視圖,對應(yīng)JSP 頁面。
C: Controller,即控制器,對應(yīng)Servlet。
load-on-startup Servlet
Servlet 的實(shí)例化有兩個(gè)時(shí)機(jī):用戶請求之時(shí),或應(yīng)用啟動(dòng)之時(shí)。應(yīng)用啟動(dòng)時(shí)就啟動(dòng)的Servlet 通常是用于某些后臺服務(wù)的Servlet ,或者攔截很多請求的Servlet; 這種Servlet 通常作為應(yīng)用的基礎(chǔ)Servlet 使用,提供重要的后臺服務(wù)。如果需要Web 應(yīng)用啟動(dòng)時(shí),可使用load-on-startup 元素完成Servlet 的初始化。load-on-startup 元素只接收一個(gè)整型值,這個(gè)整型值越小, Servlet 就越優(yōu)先初始化。
訪問Servlet 的配置參數(shù)
配置Servlet 時(shí),還可以增加附加的配置參數(shù)。通過使用配置參數(shù),可以實(shí)現(xiàn)更好地解耦,避免將所有的參數(shù)以硬編碼方式寫在程序中。
訪問Servlet 配置參數(shù)要通過ServletConfig 類的實(shí)例完成, ServletConfig提供如下方法。
java.lang.String getInitParameter(java.lang.String name): 用于獲取初始化參數(shù)。
注意: JSP 的內(nèi)直對象config 就是此處的ServletConfig
自定義標(biāo)簽類
使用標(biāo)簽類,可以使用簡單的標(biāo)簽來封裝復(fù)雜的功能,從而使團(tuán)隊(duì)更好地協(xié)作開發(fā)(能讓美工人員更好地參與JSP 頁面的開發(fā))。
自定義標(biāo)簽類都必須繼承一個(gè)父類: java.Servlet.jsp.tagext.TagSupport 。除此之外,自定義標(biāo)簽類還有如下要求。
·如果標(biāo)簽類包含屬性,每個(gè)屬性都有對應(yīng)的getter 和setter 方法。
·重寫doStartTag或doEndTag方法,這兩個(gè)方法生成頁面內(nèi)容。
·如果需要在銷毀標(biāo)簽之前完成資源回收,則重寫re1ease方法。
TLD 文件
TLD 是Tag Library Definition 的縮寫,即標(biāo)簽庫定義,文件的后綴是tld ,每個(gè)TLD文件對應(yīng)一個(gè)標(biāo)簽庫,一個(gè)標(biāo)簽庫中可包含多個(gè)標(biāo)簽。TLD 文件也稱為標(biāo)簽庫定義文件。標(biāo)簽庫定義文件的根元素是taglib,它可以有多個(gè)tag 子元素,每個(gè)tag 子元素都對應(yīng)一個(gè)標(biāo)簽。
編輯了標(biāo)簽庫定義文件還不夠, Web 容器還無法加載標(biāo)簽庫定義文件。還必須在web.xml 文件中增加標(biāo)簽庫的定義。在web.xml 文件中定義標(biāo)簽庫時(shí)使用taglib 元素,該元素包含兩個(gè)子元素: taglib-uri和taglib-location,前者確定標(biāo)簽庫的URI; 后者確定標(biāo)簽庫定義文件的位置。
使用標(biāo)簽庫
使用標(biāo)簽庫分成以下兩步。
(1) 導(dǎo)入標(biāo)簽庫:使用taglib 編譯指令導(dǎo)入標(biāo)簽。
(2) 使用標(biāo)簽:在JSP 頁面中使用自定義標(biāo)簽。
taglib 的語法格式如下:
〈%@ taglib uri="tagliburi" prefix="tagPrefix" %〉
其中uri 屬性確定標(biāo)簽庫定義文件的URI,這個(gè)URI 就是在web.xml 文件中為標(biāo)簽
庫定義的URI。而prefix 屬性確定的是標(biāo)簽前綴,即在JSP 頁面中使用標(biāo)簽時(shí),該標(biāo)簽
庫負(fù)責(zé)處理的標(biāo)簽前綴。
使用標(biāo)簽的語法格式如下:
<tagPrefix : tagName tagAttribute=ntagValue n ...>
<tagBody/>
</tagPrefix>
如果該標(biāo)簽沒有標(biāo)簽體,則可以使用如下語法格式:
<tagPrefix : tagName tagAttribute=ntagValue n …/>
帶標(biāo)簽體的標(biāo)簽
帶標(biāo)簽體的標(biāo)簽,就是允許在標(biāo)簽內(nèi)嵌套標(biāo)簽,通常可用于完成一些邏輯運(yùn)算例如判斷和循環(huán)等。
帶標(biāo)簽體的標(biāo)簽需要繼承BodyTagSupport,該類包含一個(gè)bodyContent 屬性,該屬性代表標(biāo)簽體。
BodyTagSupport 還包含兩個(gè)方法。
doAfterBody: 每次處理完標(biāo)簽體后調(diào)用該方法。
void doInitBody: 開始調(diào)用標(biāo)簽體時(shí)調(diào)用該方法。
如果有必要,可以重寫這兩個(gè)方法。
在處理標(biāo)簽類的各個(gè)方法中,不同的返回值對應(yīng)不同的含義,常用的返回值有如下幾個(gè)。
SKIP_BODY: 不處理標(biāo)簽體,直接調(diào)用doEndTag方法。
SKIP_PAGE: 忽略標(biāo)簽后面的JSP 頁面。
EVAL_PAGE: 處理標(biāo)簽結(jié)束,直接處理頁面內(nèi)容。
EVAL_BODY_BUFFERED: 處理標(biāo)簽體。
EVAL_BODY_INCLUDE: 處理標(biāo)簽體,但忽略setBodyContent和doInitBody方法。
EVAL_BODY_AGAIN: 對標(biāo)簽體循環(huán)處理。
Filter
Filter 并不是一個(gè)標(biāo)準(zhǔn)的Servlet ,它不能處理用戶請求,也不能對客戶端生成響應(yīng)。主要用于對HttpServletRequest 進(jìn)行預(yù)處理,也可以對HttpServletResponse 進(jìn)行后處理,是個(gè)典型的處理鏈。
Filter 有如下幾個(gè)用處。
·在HttpServletRequest 到達(dá)Servlet 之前,攔截客戶的HttpServletRequest 。
·根據(jù)需要檢查HttpServletRequest ,也可以修改HttpServletRequest 頭和數(shù)據(jù)。
·在HttpServletResponse 到達(dá)客戶端之前,攔截HttpServletResponse 。
·根據(jù)需要檢查HttpServletResponse ,也可以修改HttpServletResponse 頭和數(shù)據(jù)。
Filter 有如下幾個(gè)種類。
·用戶授權(quán)的Filter: Filter 負(fù)責(zé)檢查用戶請求,根據(jù)請求過濾用戶非法請求。
·日志Filter: 詳細(xì)記錄某些特殊的用戶請求。
·負(fù)責(zé)解碼的Filter: 包括對非標(biāo)準(zhǔn)編碼的請求解碼。
.能改變XML 內(nèi)容的XSLTFilter 等。
一個(gè)Filter 可負(fù)責(zé)攔截多個(gè)請求或響應(yīng):一個(gè)請求或響應(yīng)也可被多個(gè)請求攔截。
創(chuàng)建一個(gè)Filter 只需兩個(gè)步驟:
(1)創(chuàng)建Filter 處理類:
(2) 在web.xml 文件中配置Filter。
創(chuàng)建Filter 類
創(chuàng)建Filter 必須實(shí)現(xiàn)javax.servle t. Filter 接口,在該接口中定義了三個(gè)方法。
void init(FilterConfig config): 用于完成Filter 的初始化。
void destroy: 用于Filter 銷毀前,完成某些資源的回收。
void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 實(shí)現(xiàn)過濾功能,該方法就是對每個(gè)請求及響應(yīng)增加的額外處理。
執(zhí)行chain.doFilter(request,reponse)方法,當(dāng)Filter 對請求過濾后,依然將請求發(fā)送到目的地址。如果檢查權(quán)限,可以在Filter 中根據(jù)用戶請求的HttpSession,判斷用戶權(quán)限是否足夠。
如果權(quán)限不夠,則調(diào)用重定向即可,無須調(diào)用chain.doFilter(request,reponse)方法。
配置Filter
Filter 的配置和Servlet 的配置非常相似,都需要配置兩個(gè)部分:
·配置Filter 名。
·配置Filter 攔截URL 模式。
區(qū)別在于, Servlet 通常只配置一個(gè)URL ,而Filter 可以同時(shí)攔截多個(gè)請求的URL。
因此,可以配置多個(gè)Filter 攔截模式。
Listener
Listener 的作用非常類似于load-on-startup Servlet。用于在Web 應(yīng)用啟動(dòng)時(shí),啟動(dòng)某些后臺程序,這些后臺程序負(fù)責(zé)為系統(tǒng)運(yùn)行提供支持。
Listener 與load-on-startup Servlet 的區(qū)別在于: Listener 的啟動(dòng)時(shí)機(jī)比load-on-startup Servlet 早,只是Listener 是Servlet 2.3 規(guī)范之后才出現(xiàn)的。
使用Listener 只需要兩個(gè)步驟:
(1) 創(chuàng)建Listener 實(shí)現(xiàn)類。
(2) 在web.xml 文件中配置Listener。
創(chuàng)建Listener 類
創(chuàng)建Li stener 類必須實(shí)現(xiàn)ServletContex tListener 接口,該接口包含兩個(gè)方法。
eontextInitialized(ServletContextEvent see): 啟動(dòng)Web 應(yīng)用時(shí),系統(tǒng)調(diào)用該Filter的方法。
eontextDestroyed(ServletContextEvent see): 關(guān)閉Web 應(yīng)用時(shí)候,系統(tǒng)調(diào)用Filter
的方法。
配置Listener
正如load-an-startup Servlet 一樣, Listener 用于啟動(dòng)Web 應(yīng)用的后臺服務(wù)程序,但不負(fù)責(zé)處理及響應(yīng)用戶請求,因此無須配置URL。
若將Listener 配置在Web 容器中(如果Web 容器支持Listener),則Listener 將隨Web 應(yīng)用的啟動(dòng)而啟動(dòng)。
配置Listener 時(shí)使用<listener/>元素,下面是配置Listener 的片段:
<!-- 配置Listener-->
<listener>
<!- 指定Listener 的實(shí)現(xiàn)類→
<listener-class>lee.ScheduleListener</listener-class>
</listener>
在上面的配置中,既無須配置Listener 的名字,也無須配置Listener 的URL 只需配置它的實(shí)現(xiàn)類即可。此時(shí)容器將自動(dòng)檢測部署在容器中的Listener,并在應(yīng)用啟動(dòng)時(shí),自動(dòng)加載所有的Listener。
版權(quán)所有,轉(zhuǎn)載請注明出處 本文出自:
版權(quán)所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處,謝謝
posted on 2012-01-29 15:50
hoojo 閱讀(2199)
評論(5) 編輯 收藏 所屬分類:
JavaEE 、
Jsp/Servlet