developerWorks 中國(guó) ??>?? Java technology ??>

Tomcat 的過濾訣竅

Servlet 2.3 規(guī)范的附加過濾為 J2EE 應(yīng)用程序提供了強(qiáng)化的性能

developerWorks
文檔選項(xiàng)
將此頁作為電子郵件發(fā)送

將此頁作為電子郵件發(fā)送

未顯示需要 JavaScript 的文檔選項(xiàng)


拓展 Tomcat 應(yīng)用

下載 IBM 開源 J2EE 應(yīng)用服務(wù)器 WAS CE 新版本 V1.1


級(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)求處理
過濾器與 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.javacom.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)行:

  1. 在 $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>
    
    																		
    																


  2. 編輯代碼區(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>
    
    																		
    																


  3. 在 $TOMCAT_HOME/webapps 目錄下創(chuàng)建一個(gè)叫做 devworks 的新目錄,并將所有 devworks 目錄下的東西(包括所有子目錄)從源代碼區(qū)復(fù)制到該位置。現(xiàn)在就準(zhǔn)備好啟動(dòng) Tomcat 4 了。

  4. 使用下面的 URL 來訪問一個(gè)簡(jiǎn)單的 index.html 頁面: http://<hostname>/devworks/index.html

    如果您使用的是 Internet Explorer,就能看見如圖 2 所示的定制的“拒絕訪問”信息。


    圖 2. IEFilter 在遇到 Internet Explorer 的運(yùn)行效果
    IEFilter 在遇到 Internet Explorer 的運(yùn)行效果

    如果您使用的是 Netscape,那就能看見如圖 3 所示的確切的 HTML 頁面。


    圖 3. IEFilter 用 Netscape 瀏覽器的瀏覽效果
    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);
     }

														
												

我們須提供自己的源于 ServletOutputStreamwrite() 方法。在此,我們當(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

  1. 編輯 $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>
    
    																		
    																


  2. 重新啟動(dòng) Tomcat。

  3. 現(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ù)中再加入一件多功能的工具。





回頁首


參考資料