級(jí)別: 初級(jí)
Sing Li
, 作家, Wrox 出版社
2001 年 6 月 09 日
新的 Java Servlet 2.3規(guī)范有不少最激動(dòng)人心的功能,其中之一便是過濾。乍一看,Servlet 2.3過濾似乎與 Apache、IIS、Netscape Web服務(wù)器及其它服務(wù)器中已有的傳統(tǒng)過濾器非常相似。事實(shí)上,Servlet 2.3過濾從結(jié)構(gòu)上來說是一個(gè)完全不同的設(shè)計(jì) -- 補(bǔ)充支持 Java平臺(tái)面向?qū)ο蟮奶匦裕蕴峁└呒?jí)別的性能。本文向您介紹了 Tomcat 4中的過濾,并展示了如何在項(xiàng)目中高效地使用過濾器。請(qǐng)點(diǎn)擊文章頂部或底部的 討論,參與
討論論壇
,與本文作者和其他讀者分享您對(duì)本文的想法。
過濾是 Tomcat 4 的新功能。(如想了解 Tomcat 的簡(jiǎn)要?dú)v史,請(qǐng)參閱
Tomcat 的故事
)。它是 Servlet 2.3 規(guī)范的一部分,并且最終將為所有支持此標(biāo)準(zhǔn)的 J2EE 容器的廠商所采用執(zhí)行。開發(fā)人員將能夠用過濾器來實(shí)現(xiàn)以前使用不便的或難以實(shí)現(xiàn)的功能,這些功能包括:
-
資源訪問(Web 頁、JSP 頁、servlet)的定制身份認(rèn)證
-
應(yīng)用程序級(jí)的訪問資源的審核和記錄
-
應(yīng)用程序范圍內(nèi)對(duì)資源的加密訪問,它建立在定制的加密方案基礎(chǔ)上
-
對(duì)被訪問資源的及時(shí)轉(zhuǎn)換,包括從 servlet 和 JSP 的動(dòng)態(tài)輸出
這個(gè)清單當(dāng)然并沒有一一羅列,但它讓您初步體驗(yàn)到了過濾所帶來的額外價(jià)值。在本文中,我們將詳細(xì)討論 Servlet 2.3 的過濾,來看一看過濾器是如何配合 J2EE 處理模型的。不像其它傳統(tǒng)的過濾方案,Servlet 2.3 過濾是建立在 嵌套調(diào)用的基礎(chǔ)上的。我們來研究一下這一差別是怎樣在架構(gòu)上與新的高性能 Tomcat 4 設(shè)計(jì)取得一致的。最后,我們將獲得一些編寫及測(cè)試兩個(gè) Servlet 2.3 過濾器的實(shí)際經(jīng)驗(yàn)。這些過濾器只完成很簡(jiǎn)單的功能,使我們得以將注意力集中于編寫過濾器以及如何將它們集成進(jìn) Web 應(yīng)用程序的機(jī)制。
作為 Web 應(yīng)用程序構(gòu)建模塊的過濾器
在物理結(jié)構(gòu)上,過濾器是 J2EE Web 應(yīng)用程序中的應(yīng)用程序級(jí)的 Java 代碼組件。除了 servlet 和 JSP 頁以外,遵循 Servlet 2.3 規(guī)范編碼的開發(fā)人員能將過濾器作為在 Web 應(yīng)用程序中加入活動(dòng)行為的機(jī)制。與在特定的 URL 上工作的 servlet 和 JSP 頁不同,過濾器接入 J2EE 容器的處理管道,并能跨越由 Web 應(yīng)用程序提供的 URL 子集(或所有 URL)進(jìn)行工作。圖 1 說明了過濾是在哪里配合 J2EE 請(qǐng)求處理的。
圖 1. 過濾器與 J2EE 請(qǐng)求處理
兼容 Servlet 2.3 的容器允許過濾器在請(qǐng)求被處理(通過 Servlet 引擎) 以前以及請(qǐng)求得到處理 以后(過濾器將可以訪問響應(yīng))訪問 Web 請(qǐng)求。
在這些情況下,過濾器可以:
-
在請(qǐng)求得到處理以前修改請(qǐng)求的標(biāo)題
-
提供它自己的請(qǐng)求版本以供處理
-
在請(qǐng)求處理以后和被傳回給用戶以前修改響應(yīng)
-
先取得由容器進(jìn)行的所有請(qǐng)求處理,并產(chǎn)生自己的響應(yīng)
比過濾器的可用性更為重要的是,接入 J2EE 處理管道需要?jiǎng)?chuàng)建不可移植的、容器專用的和系統(tǒng)范圍的擴(kuò)展機(jī)制(如 Tomcat 3 攔截器)。
概念上的 Tomcat 過濾
不同于在 Apache、IIS 或 Netscape 服務(wù)器中能找到的熟悉的過濾機(jī)制,Servlet 2.3 過濾器并非建立在掛鉤式函數(shù)調(diào)用上。事實(shí)上, Tomcat 4 級(jí)別的引擎架構(gòu)脫離了傳統(tǒng)的 Tomcat 3.x 版本。新的 Tomcat 4 引擎取代了在請(qǐng)求處理的不同階段調(diào)用掛鉤式方法的整體式引擎,它在內(nèi)部使用了一系列的嵌套調(diào)用、包裝請(qǐng)求及響應(yīng)。不同的過濾器和資源處理器構(gòu)成了一個(gè)鏈。
在傳統(tǒng)架構(gòu)中:
-
每次接受到請(qǐng)求,掛鉤式方法就被調(diào)用,不論它們是否執(zhí)行(有時(shí)甚至是空的)。
-
方法的作用域及并發(fā)關(guān)系(每個(gè)方法可能在不同的線程上被調(diào)用)不允許在處理相同的請(qǐng)求時(shí)簡(jiǎn)單、高效地共享不同掛鉤式方法調(diào)用間的變量和信息。
在新架構(gòu)中:
-
嵌套的方法調(diào)用通過一系列過濾器實(shí)現(xiàn),它僅有應(yīng)用于當(dāng)前請(qǐng)求的過濾器組成;基于掛鉤式調(diào)用的傳統(tǒng)執(zhí)行方式需要在處理短句中調(diào)用掛鉤式例程,即使一個(gè)特定短句的處理邏輯不起任何作用。
-
局部變量在實(shí)際的過濾方法返回之前都作保留,并且可用(因?yàn)樯嫌芜^濾器的調(diào)用總在堆棧上,等待后續(xù)調(diào)用的返回)。
這一新架構(gòu)為今后的 Tomcat 性能調(diào)整與優(yōu)化提供了一個(gè)新的、更 對(duì)象友好的基礎(chǔ)。Servlet 2.3 過濾器是這個(gè)新的內(nèi)部架構(gòu)的自然擴(kuò)展。該架構(gòu)為 Web 應(yīng)用程序設(shè)計(jì)人員提供了一個(gè)可移植的執(zhí)行過濾行為的方法。
調(diào)用鏈
所有過濾器都服從調(diào)用的過濾器鏈,并通過定義明確的接口得到執(zhí)行。一個(gè)執(zhí)行過濾器的 Java 類必須執(zhí)行這一 javax.servlet.Filter
接口。這一接口含有三個(gè)過濾器必須執(zhí)行的方法:
-
doFilter(ServletRequest, ServletResponse, FilterChain)
:這是一個(gè)完成過濾行為的方法。這同樣是上游過濾器調(diào)用的方法。引入的 FilterChain
對(duì)象提供了后續(xù)過濾器所要調(diào)用的信息。
-
init(FilterConfig)
:這是一個(gè)容器所調(diào)用的初始化方法。它保證了在第一次 doFilter()
調(diào)用前由容器調(diào)用。您能獲取在 web.xml 文件中指定的初始化參數(shù)。
-
destroy()
:容器在破壞過濾器實(shí)例前, doFilter()
中的所有活動(dòng)都被該實(shí)例終止后,調(diào)用該方法。
請(qǐng)注意:
Filter
接口的方法名及語義在最近的幾個(gè) beta 周期中曾有過不斷的改變。Servlet 2.3 規(guī)范仍未處于最后的草案階段。在 Beta 1 中,該接口包括 setFilterConfig()
和 getFilterConfig()
方法,而不是 init()
和 destroy()
。
嵌套調(diào)用在 doFilter()
方法執(zhí)行中發(fā)生。除非您建立一個(gè)過濾器明確阻止所有后續(xù)處理(通過其它過濾器及資源處理器),否則過濾器一定會(huì)在 doFilter
方法中作以下的調(diào)用:
FilterChain.doFilter(request, response);
|
安裝過濾器:定義與映射
容器通過 Web 應(yīng)用程序中的配置描述符 web.xml 文件了解過濾器。有兩個(gè)新的標(biāo)記與過濾器相關(guān): <filter>
和 <filter-mapping>
。應(yīng)該指定它們?yōu)?web.xml 文件內(nèi) <web-app>
標(biāo)記的子標(biāo)記。
過濾器定義的元素
<filter>
標(biāo)記是一個(gè)過濾器定義,它必定有一個(gè) <filter- name>
和 <filter-class>
子元素。 <filter-name>
子元素給出了一個(gè)與過濾器實(shí)例相關(guān)的、基于文本的名字。 <filter-class>
指定了由容器載入的實(shí)際類。您能隨意地包含一個(gè) <init-param>
子元素為過濾器實(shí)例提供初始化參數(shù)。例如,下面的過濾器定義指定了一個(gè)叫做 IE Filter
的過濾器:
清單 1. 過濾器定義標(biāo)記
<web-app>
<filter>
<filter-name>IE Filter</filter-name>
<filter-class>com.ibm.devworks.filters.IEFilter</filter-class>
</filter>
</web-app>
|
容器處理 web.xml 文件時(shí),它通常為找到的每個(gè)過濾器定義創(chuàng)建一個(gè)過濾器實(shí)例。這一實(shí)例用來服務(wù)所有的可用 URL 請(qǐng)求;因此,以線程安全的方式編寫過濾器是最為重要的。
過濾器映射及子元素
<filter-mapping>
標(biāo)記代表了一個(gè)過濾器的映射,指定了過濾器會(huì)對(duì)其產(chǎn)生作用的 URL 的子集。它必須有一個(gè) <filter-name>
子元素與能找到您希望映射的過濾器的過濾器定義相對(duì)應(yīng)。接下來,您可以使用 <servlet-name>
或 <url-pattern>
子元素來指定映射。 <servlet-name>
指定了一個(gè)過濾器應(yīng)用的 servlet (在 web.xml 文件中的其它地方已定義)。您能使用 <url-pattern>
來指定一個(gè)該過濾器應(yīng)用的 URL 的子集。例如, /* 的樣式用來代表該過濾器映射應(yīng)用于該應(yīng)用程序用到的每個(gè) URL,而 /dept/humanresources/* 的樣式則表明該過濾器映射只應(yīng)用于人力資源部專有的 URL。
容器使用這些過濾器映射來確定一個(gè)特定的過濾器是否應(yīng)參與某個(gè)特定的請(qǐng)求。清單 1 是為應(yīng)用程序的所有 URL 定義的應(yīng)用于 IE Filter
的一個(gè)過濾器映射:
清單 2. 過濾器映射標(biāo)記
<filter-mapping>
<filter-name>IE Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
|
創(chuàng)建一個(gè)簡(jiǎn)單的過濾器
現(xiàn)在該來定義我們的第一個(gè)過濾器了。這是一個(gè)不重要的過濾器,檢查請(qǐng)求標(biāo)題以確定是不是使用 Internet Explorer 瀏覽器來查看 URL 的。如果是 Internet Explorer 瀏覽器,過濾器就顯示“拒絕訪問”的信息。盡管操作并不重要,但這個(gè)示例演示了:
-
一個(gè)過濾器的一般剖析
-
一個(gè)在請(qǐng)求到達(dá)資源處理器前檢查其標(biāo)題信息的過濾器
-
如何編寫一個(gè)過濾器來阻止基于運(yùn)行時(shí)間檢測(cè)到的條件(驗(yàn)證參數(shù)、源 IP、時(shí)間…等等)的后續(xù)處理
此過濾器的源代碼作為 IEFilter.java
, com.ibm.devworks.filters
包的一部分位于源代碼發(fā)布區(qū)中(請(qǐng)參閱
參考資料
)。現(xiàn)在就讓我們來仔細(xì)研究一下該過濾器的代碼。
清單 3. 使用 Filter 接口
public final class IEFilter implements Filter {
private FilterConfig filterConfig = null;
|
所有的過濾器都須執(zhí)行 Filter
接口。我們創(chuàng)建了一個(gè)局部變量以容納由容器在初始化過濾器時(shí)傳遞進(jìn)來的 filterConfig
。這有時(shí)發(fā)生在第一次調(diào)用 doFilter()
前。
清單 4. doFilter 方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String browserDet =
((HttpServletRequest) request).getHeader("User-Agent").toLowerCase();
if ( browserDet.indexOf("msie") != -1) {
PrintWriter out = response.getWriter();
out.println("<html><head></head><body>");
out.println("<h1>Sorry, page cannot be displayed!</h1>");
out.println("</body></html>");
out.flush();
return;
}
|
doFilter()
完成了大部分工作。我們來檢查一下叫做“用戶代理”標(biāo)題的請(qǐng)求標(biāo)題。所有的瀏覽器都提供這個(gè)標(biāo)題。我們將其轉(zhuǎn)換成小寫字母,然后查找說明問題的標(biāo)識(shí)字符串 "msie"。如果檢測(cè)到了 Internet Explorer,我們就從響應(yīng)對(duì)象中獲取一個(gè) PrintWriter
來寫出自己的響應(yīng)。在寫出了定制的響應(yīng)后,方法無需連到其它過濾器就能返回。這就是過濾器阻止后續(xù)處理的方法。
如果瀏覽器并非 Internet Explorer,我們就能進(jìn)行正常的鏈?zhǔn)讲僮鳎尯罄m(xù)過濾器和處理器能在得到請(qǐng)求時(shí)獲得執(zhí)行的機(jī)會(huì):
清單 5. 進(jìn)行正常鏈?zhǔn)讲僮?/font>
chain.doFilter(request, response);
}
|
隨后,我們粗略地執(zhí)行該過濾器中的 init()
和 destroy()
方法:
清單 6. init() 和 destroy() 方法
public void destroy() {
}
public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
}
|
測(cè)試 IEFilter
假設(shè)您安裝了 Tomcat 4 beta 3 (或更新版本)并能使用,請(qǐng)按下列步驟啟動(dòng) IEFilter
并運(yùn)行:
-
在 $TOMCAT_HOME/conf 目錄下的 server.xml 文件里創(chuàng)建一個(gè)新的應(yīng)用程序上下文,如下所示:
<!-- Tomcat Examples Context -->
<Context path="/examples" docBase="examples" debug="0"
reloadable="true">
...
</Context>
<Context path="/devworks" docBase="devworks" debug="0"
reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="localhost_devworks_log." suffix=".txt"
timestamp="true"/>
</Context>
|
-
編輯代碼區(qū)的 devworks/WEB-INF 下的 web.xml 文件,以包括下列的過濾器定義及映射:
<web-app>
<filter>
<filter-name>IE Filter</filter-name>
<filter-class>com.ibm.devworks.filters.IEFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>IE Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
-
在 $TOMCAT_HOME/webapps 目錄下創(chuàng)建一個(gè)叫做 devworks 的新目錄,并將所有 devworks 目錄下的東西(包括所有子目錄)從源代碼區(qū)復(fù)制到該位置。現(xiàn)在就準(zhǔn)備好啟動(dòng) Tomcat 4 了。
-
使用下面的 URL 來訪問一個(gè)簡(jiǎn)單的 index.html 頁面: http://<hostname>/devworks/index.html
如果您使用的是 Internet Explorer,就能看見如圖 2 所示的定制的“拒絕訪問”信息。
圖 2. IEFilter 在遇到 Internet Explorer 的運(yùn)行效果
如果您使用的是 Netscape,那就能看見如圖 3 所示的確切的 HTML 頁面。
圖 3. IEFilter 用 Netscape 瀏覽器的瀏覽效果
|
|
編寫轉(zhuǎn)換資源的過濾器
現(xiàn)在該來試一下更復(fù)雜的過濾器了。該過濾器:
-
從過濾器定義的實(shí)例初始化參數(shù)中讀取一組 "search" 及 "replace" 文本
-
過濾被訪問的 URL,將出現(xiàn)的第一個(gè) "search" 文本替代為 "replace" 文本
在我們深入研究這個(gè)過濾器的過程中,您將對(duì)內(nèi)容轉(zhuǎn)換/替代過濾器的架構(gòu)加深了解。相同的架構(gòu)能用于任何加密、壓縮及轉(zhuǎn)換(如由 XSLT 轉(zhuǎn)換來的 SML)過濾器。
核心機(jī)密是在鏈?zhǔn)教幚淼倪^程中傳遞一個(gè)定制的響應(yīng)對(duì)象的包裝版本。該定制的包裝響應(yīng)對(duì)象須隱藏原響應(yīng)對(duì)象(從而對(duì)其實(shí)現(xiàn) 包裝),并提供一個(gè)定制的流以供后續(xù)處理器寫入。如果工作(文本替換、轉(zhuǎn)換、壓縮、加密…等)能迅速完成,定制流的執(zhí)行就能中止后續(xù)記錄并完成需要的工作。然后定制的流就會(huì)將經(jīng)轉(zhuǎn)換的數(shù)據(jù)寫入包裝的響應(yīng)對(duì)象(也就是說,簡(jiǎn)單的字符替換加密)。如果工作無法迅速完成,定制的流就需等待,直到后續(xù)處理器完成對(duì)流的寫入(也就是說,當(dāng)其關(guān)閉或刷新流時(shí))。然后它才完成轉(zhuǎn)換工作,并將經(jīng)轉(zhuǎn)換的輸出結(jié)果寫入“真正的”響應(yīng)中。
在我們的過濾器( ReplaceTextFilter
)中,定制的包裝響應(yīng)對(duì)象叫作 ReplaceTextWrapper
。定制流的執(zhí)行叫做 ReplaceTextStream
。您能在 com.ibm.devworks.filters
包中的 ReplaceTextFilter.java 文件里找到源代碼。(請(qǐng)參閱
參考資料
)。現(xiàn)在就讓我們來研究一下源代碼吧。
清單 7. ReplaceTextStream 類
class ReplaceTextStream extends ServletOutputStream {
private OutputStream intStream;
private ByteArrayOutputStream baStream;
private boolean closed = false;
private String origText;
private String newText;
public ReplaceTextStream(OutputStream outStream,
String searchText,
String replaceText) {
intStream = outStream;
baStream = new ByteArrayOutputStream();
origText = searchText;
newText = replaceText;
}
|
這是定制的輸出流代碼。 intStream
變量包含了對(duì)來自響應(yīng)對(duì)象的實(shí)際流的引用。 baStream
是我們輸出流的緩沖版本,后續(xù)處理器就寫入這里。 closed
標(biāo)記標(biāo)明了 close()
是否在此實(shí)例流中被調(diào)用。構(gòu)造器將來自響應(yīng)對(duì)象的流引用存儲(chǔ)起來并創(chuàng)建了緩沖流。它還將文本字符串存儲(chǔ)起來供以后的替代操作使用。
清單 8. write() 方法
public void write(int i) throws java.io.IOException {
baStream.write(i);
}
|
我們須提供自己的源于 ServletOutputStream
的 write()
方法。在此,我們當(dāng)然是寫入緩沖流。所有來自后續(xù)處理器的更高級(jí)輸出方法都將以最低級(jí)別使用該方法,以保證所有的寫入都指向緩沖流。
清單 9. close() 及 flush() 方法
public void close() throws java.io.IOException {
if (!closed) {
processStream();
intStream.close();
closed = true;
}
}
public void flush() throws java.io.IOException {
if (baStream.size() != 0) {
if (! closed) {
processStream(); // need to synchronize the flush!
baStream = new ByteArrayOutputStream();
}
}
}
|
close()
及 flush()
方法是我們完成轉(zhuǎn)換的語句。根據(jù)后續(xù)處理器不同,其中的一個(gè)或兩個(gè)程序都有可能被調(diào)用。我們使用布爾型的 closed
標(biāo)識(shí)來避免異常情況。請(qǐng)注意,我們將實(shí)際的替代工作委托給了 processStream()
方法。
清單 10. processStream() 方法
public void processStream() throws java.io.IOException {
intStream.write(replaceContent(baStream.toByteArray()));
intStream.flush();
}
|
processStream()
方法將經(jīng)轉(zhuǎn)換的輸出結(jié)果從 baStream
寫入其已經(jīng)配有的 intStream
中去。轉(zhuǎn)換工作獨(dú)立于 replaceContent()
方法。
清單 11. replaceContent() 方法
public byte [] replaceContent(byte [] inBytes) {
String retVal ="";
String firstPart="";
String tpString = new String(inBytes);
String srchString = (new String(inBytes)).toLowerCase();
int endBody = srchString.indexOf(origText);
if (endBody != -1) {
firstPart = tpString.substring(0, endBody);
retVal = firstPart + newText +
tpString.substring(endBody + origText.length());
} else {
retVal=tpString;
}
return retVal.getBytes();
}
}
|
replaceContent()
是發(fā)生搜索與替換的語句。它將一個(gè)字節(jié)數(shù)組作為輸入并返回一個(gè)字節(jié)數(shù)組,創(chuàng)建一個(gè)原始的概念接口。事實(shí)上,我們能通過替換該方法中的邏輯部分來完成任何形式的轉(zhuǎn)換。這里,我們進(jìn)行非常簡(jiǎn)單的文本替換。
清單 12. ReplaceTextWrapper 類
class ReplaceTextWrapper extends HttpServletResponseWrapper {
private PrintWriter tpWriter;
private ReplaceTextStream tpStream;
public ReplaceTextWrapper(ServletResponse inResp, String searchText,
String replaceText)
throws java.io.IOException {
super((HttpServletResponse) inResp);
tpStream = new ReplaceTextStream(inResp.getOutputStream(),
searchText,
replaceText);
tpWriter = new PrintWriter(tpStream);
}
public ServletOutputStream getOutputStream() throws java.io.IOException {
return tpStream;
}
public PrintWriter getWriter() throws java.io.IOException {
return tpWriter;
}
}
|
我們定制的包裝響應(yīng)能方便地從幫助類 HttpServletResponseWrapper
中導(dǎo)出。這一類粗略地執(zhí)行許多方法,允許我們簡(jiǎn)單地覆蓋 getOutputStream()
方法以及 getWriter()
方法,提供了定制輸出流的實(shí)例。
清單 13. ReplaceTextWrapper() 方法
public final class ReplaceTextFilter implements Filter {
private FilterConfig filterConfig = null;
private String searchText = ".";
private String replaceText = ".";
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
ReplaceTextWrapper myWrappedResp = new ReplaceTextWrapper( response,
searchText, replaceText);
chain.doFilter(request, myWrappedResp);
myWrappedResp.getOutputStream().close();
}
public void destroy() {
}
|
最后,還有過濾器本身。它所做的不過是使用 FilterChain
為遞交響應(yīng)后續(xù)創(chuàng)建一個(gè)定制的包裝響應(yīng)實(shí)例,如下所示:
清單 14. 創(chuàng)建一個(gè)定制的包裝響應(yīng)實(shí)例
public void init(FilterConfig filterConfig) {
String tpString;
if (( tpString = filterConfig.getInitParameter("search") ) != null)
searchText = tpString;
if (( tpString = filterConfig.getInitParameter("replace") ) != null)
replaceText = tpString;
this.filterConfig = filterConfig;
}
}
|
在 init
方法中,我們?nèi)』亓诉^濾器定義中指定的初始參數(shù)。 filterConfig
對(duì)象中的 getInitParameter()
方法便于用來實(shí)現(xiàn)這個(gè)目的。
測(cè)試 ReplaceTextFilter
假如您使用先前提及的步驟測(cè)試了 IEFilter
,并將所有文件復(fù)制到了 $TOMCAT/webapps/devworks 下,您就能用以下的步驟來測(cè)試 ReplaceTextFilter
:
-
編輯 $TOMCAT/wepapps/devworks/WEB-INF 目錄下的 web.xml 文件,以包含下列過濾器的定義及映射:
<web-app>
<filter>
<filter-name>Replace Text Filter</filter-name>
<filter-class>com.ibm.devworks.filters.ReplaceTextFilter</filter-class>
<init-param>
<param-name>search</param-name>
<param-value>cannot</param-value>
</init-param>
<init-param>
<param-name>replace</param-name>
<param-value>must not</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Replace Text Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
-
重新啟動(dòng) Tomcat。
-
現(xiàn)在,請(qǐng)用下面的 URL 來訪問 index.html 頁面: http://<host name>:8080/devworks/index.html
請(qǐng)注意, ReplaceTextFilter
是如何迅速地將 cannot變?yōu)?must not 的。想確信過濾使用了所有資源,您可以嘗試編寫輸出結(jié)果含有字符串 cannot的 JSP 頁或 servlet。
過濾器鏈排列順序的重要性
過濾器鏈?zhǔn)脚帕械捻樞蛉Q于 web.xml 描述信息內(nèi) <filter-mapping>
語句的順序。在大多數(shù)情況下,過濾器鏈?zhǔn)脚帕械捻樞蚴欠浅V匾摹R簿褪钦f,在應(yīng)用 A 過濾器前使用 B 過濾器與在使用 B 過濾器前使用 A 過濾器所得到的結(jié)果是完全不同的。如果一個(gè)應(yīng)用程序中使用了一個(gè)以上的過濾器,那么在寫入 <filter-mapping>
語句的時(shí)候要小心。
我們能輕易地通過排列 web.xml 文件中 <filter-mapping>
的順序看到這一效果:
清單 15. 過濾的順序 -- IE Filter 為先
<web-app>
<filter-mapping>
<filter-name>IE Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Replace Text Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
現(xiàn)在,用 Internet Explorer 載入 index.html 頁。您能看到由于 IE Filter
處于過濾器鏈中的第一位,所以 Replace Text Filter
沒有機(jī)會(huì)執(zhí)行。因此,輸出的信息是 "Sorry, page cannot be displayed!"
現(xiàn)在,將 <filter-mapping>
標(biāo)記的順序顛倒過來,變?yōu)椋?
清單 16. 過濾的順序 -- Replace Text Filter 為先
<web-app>
<filter-mapping>
<filter-name>Replace Text Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>IE Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
再次用 Internet Explorer 載入 index.html 頁面。這次, Replace Text Filter
先執(zhí)行,將包裝的響應(yīng)對(duì)象提供給 IE Filter
。在 IE Filter
寫入了其定制的響應(yīng)后,專用的響應(yīng)對(duì)象在輸出結(jié)果到達(dá)最終用戶處以前完成轉(zhuǎn)換。故而,我們看到了這條信息:Sorry, page must not be displayed!
在應(yīng)用程序中使用過濾器
寫這篇文章的時(shí)候, Tomcat 4 正處于 beta 周期的后期,正式發(fā)行的日子已為期不遠(yuǎn)。主要的 J2EE 容器廠商都準(zhǔn)備好了將 Servlet 2.3 規(guī)范整合到其產(chǎn)品中去。對(duì)于 Servlet 2.3 過濾器如何工作有一個(gè)基本的了解有助于您在設(shè)計(jì)及編寫基于 J2EE 的應(yīng)用程序時(shí)往自己的工具庫(kù)中再加入一件多功能的工具。
參考資料