用Java Servlets 2.4實現(xiàn)過濾
Posted on 2006-10-24 10:03 daniel-shen 閱讀(10) 評論(0) 編輯 收藏 引用 網(wǎng)摘 所屬分類: javaServlet 過濾器是可插入的 Web 組件,它允許我們實現(xiàn) Web 應(yīng)用程序中的預(yù)處理和后期處理邏輯。過濾器支持 servlet 和 JSP 頁面的基本請求處理功能,比如日志記錄、性能、安全、會話處理、XSLT 轉(zhuǎn)換,等等。 過濾器最初是隨 Java Servlet 2.3 規(guī)范發(fā)布的,最近定稿的 2.4 規(guī)范對它進行了重大升級。在此我將向您介紹 Servlet 過濾器的基礎(chǔ)知識 ―― 比如總體的體系結(jié)構(gòu)設(shè)計、實現(xiàn)細(xì)節(jié),以及在 J2EE Web 應(yīng)用程序中的典型應(yīng)用,還會涉及一些預(yù)計最新的 Servlet 規(guī)范將會提供的擴展功能。
Servlet 過濾器是什么?
Servlet 過濾器是小型的 Web 組件,它們攔截請求和響應(yīng),以便查看、提取或以某種方式操作正在客戶機和服務(wù)器之間交換的數(shù)據(jù)。過濾器是通常封裝了一些功能的 Web 組件,這些功能雖然很重要,但是對于處理客戶機請求或發(fā)送響應(yīng)來說不是決定性的。典型的例子包括記錄關(guān)于請求和響應(yīng)的數(shù)據(jù)、處理安全協(xié)議、管理會話屬性,等等。過濾器提供一種面向?qū)ο蟮哪K化機制,用以將公共任務(wù)封裝到可插入的組件中,這些組件通過一個配置文件來聲明,并動態(tài)地處理。
Servlet 過濾器中結(jié)合了許多元素,從而使得過濾器成為獨特、強大和模塊化的 Web 組件。也就是說,Servlet 過濾器是:
* 聲明式的:過濾器通過 Web 部署描述符(web.xml)中的 XML 標(biāo)簽來聲明。這樣允許添加和刪除過濾器,而無需改動任何應(yīng)用程序代碼或 JSP 頁面。
* 動態(tài)的:過濾器在運行時由 Servlet 容器調(diào)用來攔截和處理請求和響應(yīng)。
* 靈活的:過濾器在 Web 處理環(huán)境中的應(yīng)用很廣泛,涵蓋諸如日志記錄和安全等許多最公共的輔助任務(wù)。過濾器還是靈活的,因為它們可用于對來自客戶機的直接調(diào)用執(zhí)行預(yù)處理和后期處理,以及處理在防火墻之后的 Web 組件之間調(diào)度的請求。最后,可以將過濾器鏈接起來以提供必需的功能。
* 模塊化的:通過把應(yīng)用程序處理邏輯封裝到單個類文件中,過濾器從而定義了可容易地從請求/響應(yīng)鏈中添加或刪除的模塊化單元。
* 可移植的:與 Java 平臺的其他許多方面一樣,Servlet 過濾器是跨平臺和跨容器可移植的,從而進一步支持了 Servler 過濾器的模塊化和可重用本質(zhì)。
* 可重用的:歸功于過濾器實現(xiàn)類的模塊化設(shè)計,以及聲明式的過濾器配置方式,過濾器可以容易地跨越不同的項目和應(yīng)用程序使用。
* 透明的:在請求/響應(yīng)鏈中包括過濾器,這種設(shè)計是為了補充(而不是以任何方式替代)servlet 或 JSP 頁面提供的核心處理。因而,過濾器可以根據(jù)需要添加或刪除,而不會破壞 servlet 或 JSP 頁面。
所以 Servlet 過濾器是通過一個配置文件來靈活聲明的模塊化可重用組件。過濾器動態(tài)地處理傳入的請求和傳出的響應(yīng),并且無需修改應(yīng)用程序代碼就可以透明地添加或刪除它們。最后,過濾器獨立于任何平臺或者 Servlet 容器,從而允許將它們?nèi)菀椎夭渴鸬饺魏蜗嗳莸?J2EE 環(huán)境中。
在接下來的幾小節(jié)中,我們將進一步考察 Servlet 過濾器機制的總體設(shè)計,以及實現(xiàn)、配置和部署過濾器所涉及的步驟。我們還將探討 Servlet 過濾器的一些實際應(yīng)用,最后簡要考察一下模型-視圖-控制器(MVC)體系結(jié)構(gòu)中包含的 Servlet 過濾器,從而結(jié)束本文的討論。
Servlet 過濾器體系結(jié)構(gòu)
正如其名稱所暗示的, Servlet 過濾器用于攔截傳入的請求和/或傳出的響應(yīng),并監(jiān)視、修改或以某種方式處理正在通過的數(shù)據(jù)流。過濾器是自包含、模塊化的組件,可以將它們添加到請求/響應(yīng)鏈中,或者在無需影響應(yīng)用程序中其他 Web 組件的情況下刪除它們。過濾器僅只是改動請求和響應(yīng)的運行時處理,因而不應(yīng)該將它們直接嵌入 Web 應(yīng)用程序框架,除非是通過 Servlet API 中良好定義的標(biāo)準(zhǔn)接口來實現(xiàn)。
Web 資源可以配置為沒有過濾器與之關(guān)聯(lián)(這是默認(rèn)情況)、與單個過濾器關(guān)聯(lián)(這是典型情況),甚至是與一個過濾器鏈相關(guān)聯(lián)。那么過濾器究竟做什么呢? 像 servlet 一樣,它接受請求并響應(yīng)對象。然后過濾器會檢查請求對象,并決定將該請求轉(zhuǎn)發(fā)給鏈中的下一個組件,或者中止該請求并直接向客戶機發(fā)回一個響應(yīng)。如果請求被轉(zhuǎn)發(fā)了,它將被傳遞給鏈中的下一個資源(另一個過濾器、servlet 或 JSP 頁面)。在這個請求設(shè)法通過過濾器鏈并被服務(wù)器處理之后,一個響應(yīng)將以相反的順序通過該鏈發(fā)送回去。這樣就給每個過濾器都提供了根據(jù)需要處理響應(yīng)對象的機會。
當(dāng)過濾器在 Servlet 2.3 規(guī)范中首次引入時,它們只能過濾 Web 客戶機和客戶機所訪問的指定 Web 資源之間的內(nèi)容。如果該資源然后將請求調(diào)度給其他 Web 資源,那就不能向幕后委托的任何請求應(yīng)用過濾器。2.4 規(guī)范消除了這個限制。Servlet 過濾器現(xiàn)在可以應(yīng)用于 J2EE Web 環(huán)境中存在請求和響應(yīng)對象的任何地方。因此,Servlet 過濾器可以應(yīng)用在客戶機和 servlet 之間、servlet 和 servlet 或 JSP 頁面之間,以及所包括的每個 JSP 頁面之間。這才是我所稱的強大能力和靈活性!
實現(xiàn)一個 Servlet 過濾器
他們說“好事多磨& rdquo;。我不知道“他們”指的是誰,或者這句古老的諺語究竟有多真實,但是實現(xiàn)一個 Servlet 過濾器的確要經(jīng)歷三個步驟。首先要編寫過濾器實現(xiàn)類的程序,然后要把該過濾器添加到 Web 應(yīng)用程序中(通過在 Web 部署描述符 /web.xml 中聲明它),最后要把過濾器與應(yīng)用程序一起打包并部署它。我們將詳細(xì)研究這其中的每個步驟。
1. 編寫實現(xiàn)類的程序
過濾器 API 包含 3 個簡單的接口(又是數(shù)字 3!),它們整潔地嵌套在 javax.servlet 包中。那 3 個接口分別是 Filter 、 FilterChain 和 FilterConfig 。從編程的角度看,過濾器類將實現(xiàn) Filter 接口,然后使用這個過濾器類中的 FilterChain 和 FilterConfig 接口。該過濾器類的一個引用將傳遞給 FilterChain 對象,以允許過濾器把控制權(quán)傳遞給鏈中的下一個資源。 FilterConfig 對象將由容器提供給過濾器,以允許訪問該過濾器的初始化數(shù)據(jù)。
為了與我們的三步模式保持一致,過濾器必須運用三個方法,以便完全實現(xiàn) Filter 接口:
* init() :這個方法在容器實例化過濾器時被調(diào)用,它主要設(shè)計用于使過濾器為處理做準(zhǔn)備。該方法接受一個 FilterConfig 類型的對象作為輸入。
* doFilter() :與 servlet 擁有一個 service() 方法(這個方法又調(diào)用 doPost() 或者 doGet() )來處理請求一樣,過濾器擁有單個用于處理請求和響應(yīng)的方法―― doFilter() 。這個方法接受三個輸入?yún)?shù):一個 ServletRequest 、 response 和一個 FilterChain 對象。
* destroy() :正如您想像的那樣,這個方法執(zhí)行任何清理操作,這些操作可能需要在自動垃圾收集之前進行。
清單 1 展示了一個非常簡單的過濾器,它跟蹤滿足一個客戶機的 Web 請求所花的大致時間。
清單 1. 一個過濾器類實現(xiàn)
import javax.servlet.*;
import java.util.*;
import java.io.*;
public class TimeTrackFilter implements Filter
{
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
}
public void destroy()
{
this.filterConfig = null;
}
public void doFilter( ServletRequest request,ServletResponse response, FilterChain chain ) throws IOException,ServletException
{
Date startTime, endTime;
double totalTime;
startTime = new Date();
// Forward the request to the next resource in the chain
chain.doFilter(request, wrapper);
// -- Process the response -- \\
// Calculate the difference between the start time and end time
endTime = new Date();
totalTime = endTime.getTime() - startTime.getTime();
totalTime = totalTime / 1000; //Convert from milliseconds to seconds
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
writer.println();
writer.println("===============");
writer.println("Total elapsed time is: " + totalTime + " seconds." );
writer.println("===============");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().
log(sw.getBuffer().toString());
}
}
這個過濾器的生命周期很簡單,不管怎樣,我們還是研究一下它吧:
初始化
當(dāng)容器第一次加載該過濾器時, init() 方法將被調(diào)用。該類在這個方法中包含了一個指向 FilterConfig 對象的引用。我們的過濾器實際上并不需要這樣做,因為其中沒有使用初始化信息,這里只是出于演示的目的。
過濾
過濾器的大多數(shù)時間都消耗在這里。 doFilter() 方法被容器調(diào)用,同時傳入分別指向這個請求/響應(yīng)鏈中的 ServletRequest 、 ServletResponse 和 FilterChain 對象的引用。然后過濾器就有機會處理請求,將處理任務(wù)傳遞給鏈中的下一個資源(通過調(diào)用 FilterChain 對象引用上的 doFilter() 方法),之后在處理控制權(quán)返回該過濾器時處理響應(yīng)。
析構(gòu)
容器緊跟在垃圾收集之前調(diào)用 destroy() 方法,以便能夠執(zhí)行任何必需的清理代碼。
2. 配置 Servlet 過濾器
過濾器通過 web.xml 文件中的兩個 XML 標(biāo)簽來聲明。 <filter> 標(biāo)簽定義過濾器的名稱,并且聲明實現(xiàn)類和 init() 參數(shù)。 <filter-mapping> 標(biāo)簽將過濾器與 servlet 或 URL 模式相關(guān)聯(lián)。
清單 2 摘自一個 web.xml 文件,它展示了如何聲明過濾器的包含關(guān)系:
清單 2. 在 web.xml 中聲明一個過濾器
<filter>
<filter-name>Page Request Timer</filter-name>
<filter-class>TimeTrackFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Page Request Timer</filter-name>
<servlet-name>Main Servlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>Main Servlet</servlet-name>
<servlet-class>MainServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Main Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
上面的代碼示例聲明了一個過濾器("Page Request Timer"),并把它映射到一個 servlet("Main Servlet")。然后為該 servlet 定義了一個映射,以便把每個請求(由通配符指定)都發(fā)送到該 servlet。這是控制器組件的典型映射聲明。您應(yīng)該注意這些聲明的順序,因為千萬不能背離這些元素的順序。
3. 部署 Servlet 過濾器
事實上,與 Web 應(yīng)用程序一起部署過濾器絕對不涉及任何復(fù)雜性。只需把過濾器類和其他 Web 組件類包括在一起,并像您通常所做的那樣把 web.xml 文件(連同過濾器定義和過濾器映射聲明)放進 Web 應(yīng)用程序結(jié)構(gòu)中,servlet 容器將處理之后的其他所有事情。
過濾器的許多應(yīng)用
您在 J2EE Web 應(yīng)用程序中利用過濾器的能力,僅受到您自己的創(chuàng)造性和應(yīng)用程序設(shè)計本領(lǐng)的限制。在適合使用裝飾過濾器模式或者攔截器模式的任何地方,您都可以使用過濾器。過濾器的一些最普遍的應(yīng)用如下:
# 加載:對于到達(dá)系統(tǒng)的所有請求,過濾器收集諸如瀏覽器類型、一天中的時間、轉(zhuǎn)發(fā) URL 等相關(guān)信息,并對它們進行日志記錄。
# 性能:過濾器在內(nèi)容通過線路傳來并在到達(dá) servlet 和 JSP 頁面之前解壓縮該內(nèi)容,然后再取得響應(yīng)內(nèi)容,并在將響應(yīng)內(nèi)容發(fā)送到客戶機機器之前將它轉(zhuǎn)換為壓縮格式。
# 安全:過濾器處理身份驗證令牌的管理,并適當(dāng)?shù)叵拗瓢踩Y源的訪問,提示用戶進行身份驗證和/或?qū)⑺麄冎敢降谌竭M行身份驗證。過濾器甚至能夠管理訪問控制列表(Access Control List,ACL),以便除了身份驗證之外還提供授權(quán)機制。將安全邏輯放在過濾器中,而不是放在 servlet 或者 JSP 頁面中,這樣提供了巨大的靈活性。在開發(fā)期間,過濾器可以關(guān)閉(在 web.xml 文件中注釋掉)。在生產(chǎn)應(yīng)用中,過濾器又可以再次啟用。此外還可以添加多個過濾器,以便根據(jù)需要提高安全、加密和不可拒絕的服務(wù)的等級。
# 會話處理:將 servlet 和 JSP 頁面與會話處理代碼混雜在一起可能會帶來相當(dāng)大的麻煩。使用過濾器來管理會話可以讓 Web 頁面集中精力考慮內(nèi)容顯示和委托處理,而不必?fù)?dān)心會話管理的細(xì)節(jié)。
# XSLT 轉(zhuǎn)換:不管是使用移動客戶端還是使用基于 XML 的 Web 服務(wù),無需把邏輯嵌入應(yīng)用程序就在 XML 語法之間執(zhí)行轉(zhuǎn)換的能力都絕對是無價的。
使過濾器適應(yīng) MVC 體系結(jié)構(gòu)
模型-視圖-控制器(Model-View- Controller,MVC)體系結(jié)構(gòu)是一個有效的設(shè)計,它現(xiàn)在已作為最重要的設(shè)計方法學(xué),整合到了諸如 Jakarta Struts 和 Turbine 等大多數(shù)流行的 Web 應(yīng)用框架中。過濾器旨在擴充 MVC 體系結(jié)構(gòu)的請求/響應(yīng)處理流。不管請求/響應(yīng)發(fā)生在客戶機和服務(wù)器之間,還是發(fā)生在服務(wù)器上的其他組件之間,過濾器在處理流中的應(yīng)用都是相同的。從 MVC 的觀點看,調(diào)度器組件(它或者包括在控制器組件中,或者配合控制器組件工作)把請求轉(zhuǎn)發(fā)給適當(dāng)?shù)膽?yīng)用程序組件以進行處理。這使得控制器層成為包括 Servlet 過濾器的最佳位置。通過把過濾器放在控制器組件本身的前面,過濾器可以應(yīng)用于所有請求,或者通過將它放在控制器/調(diào)度器與模型和控制器之間,它可以應(yīng)用于單獨的 Web 組件。
MVC 體系結(jié)構(gòu)廣為傳播,并具有良好的文檔,詳細(xì)討論MVC不是本文的內(nèi)容,有興趣的讀者可自行查找相關(guān)內(nèi)容。
結(jié)束語
雖然過濾器才出現(xiàn)幾年時間,但它們本身已作為一個關(guān)鍵組件嵌入到了所有敏捷的、面向?qū)ο蟮?J2EE Web 應(yīng)用程序中。本文向您介紹了 Servlet 過濾器的使用。本文討論了過濾器的高級設(shè)計,比較了當(dāng)前規(guī)范(2.4)和以前(2.3)的模型,講述了實現(xiàn)過濾器所涉及的精確步驟,以及如何在 Web 應(yīng)用程序中聲明過濾器,然后與應(yīng)用程序一起部署它。本文還闡述了 Servlet 過濾器的一些最普遍應(yīng)用,并提到了過濾器如何適應(yīng)傳統(tǒng)的 MVC 體系結(jié)構(gòu)。