版權(quán)聲明:可以任意轉(zhuǎn)載,轉(zhuǎn)載時(shí)請務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
英文原文地址:
http://dev2dev.bea.com/pub/a/2005/05/decorators.html
中文地址
http://www.matrix.org.cn/resource/article/43/43603_Servlet_Request.html
關(guān)鍵詞:Servlet ?Request ? ?filter ?Decorator
摘要
裝飾模式是Erich ?Gamma等人所著的《設(shè)計(jì)模式:可利用面向?qū)ο筌浖幕A(chǔ)》一書中眾多模式之一。一般來說,此模式在設(shè)計(jì)Swing的程序員中比較流行,他們用它來改進(jìn)軟件。今天,即使有許多程序是基于Web應(yīng)用的,裝飾模式仍有用武之地,在J2EE的環(huán)境下也有使用的價(jià)值。
本文說明了如何將裝飾模式應(yīng)用到servlet ?request對象上。首先,提出了一個(gè)與servlet ?filter有關(guān)的問題,并解釋了隨之而引入的裝飾模式。然后,討論了如何在servlet環(huán)境下使用此模式,并列出了使用此模式的幾個(gè)比較有名的基于servlet的項(xiàng)目。最后,文章通過實(shí)現(xiàn)一個(gè)刪除空白符的filter例子,演示了裝飾模式在servlet中的使用。
簡介
? ? ? ?Servlet規(guī)范中所引入的filter令人心動(dòng)不已,因?yàn)樗肓艘粋€(gè)功能強(qiáng)大的攔截模式。Filter是這樣一種Java對象,它能在request到達(dá)servlet的服務(wù)方法之前攔截HttpServletRequest對象,而在服務(wù)方法轉(zhuǎn)移控制后又能攔截HttpServletResponse對象。你可以使用filter來實(shí)現(xiàn)特定的任務(wù),比如驗(yàn)證用戶輸入,以及壓縮web內(nèi)容。但你擬富有成效地使用過濾器的念頭卻被你不能改變HttpServletRequest對象的參數(shù)的現(xiàn)實(shí)掃了興,因?yàn)閖ava.util.Map所包裝的HttpServletRequest對象的參數(shù)是不可改變的。這極大地縮減了filter的應(yīng)用范圍。至少在一半的時(shí)間里,你希望可以改變準(zhǔn)備傳送給filter的對象。如果在HttpServletRequest對象到達(dá)Struts的action ?servlet之前,我們可以通過一個(gè)filter將用戶輸入的多余空格去掉,難道不是更美妙嗎?這樣的話,你就不必等到在Struts的action表單驗(yàn)證方法中才進(jìn)行這項(xiàng)工作了。
幸運(yùn)的是,盡管你不能改變不變對象本身,但你卻可以通過使用裝飾模式來改變其狀態(tài)。
裝飾模式
在繼承中,你可以通過繼承一個(gè)父類并覆蓋你希望改變的方法來改變對象狀態(tài)。然而,如果這個(gè)對象是由程序的另一個(gè)子模塊,例如對象工廠 ?(這里所說的工廠是工廠模式中的術(shù)語,下同。譯者注) ?或是servlet容器所產(chǎn)生的,繼承就無能為力了。
裝飾模式可用來增加一個(gè)現(xiàn)有對象的功能,或是改變其狀態(tài)。與其使用繼承方式來擴(kuò)展此類,這個(gè)模式將一個(gè)對象包裝成另外一個(gè)對象。圖1是裝飾模式的UML類圖。

圖1:裝飾模式
在圖1中,Component是一個(gè)接口,其具體實(shí)現(xiàn)是ConcreteComponent。要改變Component的狀態(tài),你可以修改ConcreteComponent或是擴(kuò)展它 ?(通過繼承或?qū)崿F(xiàn)接口的方式,譯者注)。然而,如果ConcreteComponent來自于一個(gè)工廠,你卻無計(jì)可施。你所能做的,就是創(chuàng)建一個(gè)同為實(shí)現(xiàn)了Component接口的裝飾類。在圖1中,這個(gè)裝飾類的角色就由Decorator來扮演,在程序中通常表現(xiàn)為接口或抽象類。Decorator類的一個(gè)特性就是,它有一個(gè)接收Component對象的構(gòu)造方法。你將擬裝飾的對象傳遞給這個(gè)構(gòu)造方法。在本例中,這個(gè)對象就是從工廠獲得的ConcreteComponent對象。通過將此裝飾對象傳遞給Decorator的一個(gè)類變量,你可以訪問Decorator中的任何方法。這就使你得以改變對象的狀態(tài)了。
圖1中的Decorator類不一定是接口或抽象類。如果你的程序不是很復(fù)雜,你可以將其轉(zhuǎn)化為一個(gè)具體的Decorator類。
舉個(gè)例子,考慮這樣一個(gè)簡單的消息傳遞程序,其主要部分是Messenger接口及其實(shí)現(xiàn)類MessengerImpl。讓我們假設(shè)MessengerImpl對象來自于一個(gè)工廠,因此你不能改變其狀態(tài)。如果你準(zhǔn)備增加或改變Messenger對象的功能,你可以創(chuàng)建一個(gè)MessengerDecorator類。圖2是此例子的類圖。

圖2:Messenger裝飾類
我們來看程序的代碼。列表1給出了Messenger接口的代碼,列表2是MessengerImpl類的代碼。
列表1:Messenger接口
public ?interface ?Messenger ?{ ? ?public ?String ?getMessage();}
列表2:MessengerImpl類
Messenger對象由一個(gè)名為MessengerFactory的工廠創(chuàng)建,如列表3所示。
列表3:MessengerFactory類
public ?class ?MessengerFactory ?{ ? ?public ?static ?Messenger ?getMessenger() ?{ ? ? ? ?return ?new ?MessengerImpl("secrets"); ? ?}}
對每一個(gè)所創(chuàng)建的Messenger對象,此工廠通過某個(gè)未知的操作,初始化了getMessage()方法所返回的字符串。換句話說,你不能自己創(chuàng)建Messenger對象。
在程序中,Messenger對象的主要用途是被傳遞給一個(gè)名為Util的類中的broadcast()靜態(tài)方法。列表4是Util類的代碼。
列表4:Util類
public ?class ?Util ?{ ? ?public ?static ?void ?broadcast(Messenger ?messenger) ?{ ? ? ? ?System.out.print(messenger.getMessage()); ? ?} ? ?// ?other ?methods ?here}
在你自己的類中,你可能會(huì)有這樣的代碼:
Messenger ?messenger ?= ?MessengerFactory.getMessenger();Util.broadcast(messenger);
假設(shè)你希望對broadcast()方法所打印出的消息做一小改動(dòng)。你擬將其轉(zhuǎn)為大寫,怎么做?表面上看,你可以繼承Messenger,實(shí)例化其子類,并將返回的對象傳給Util.broadcast()。但是,這種做法毫無意義,因?yàn)橹挥泄S才知道如何初始化Messenger對象,并通過其getMessage()方法返回正確的值。
使用裝飾模式,你可以創(chuàng)建一個(gè)MessengerDecorator類,如列表5所示。
列表5:MessengerDecorator類
public ?class ?MessengerDecorator ?implements ?Messenger ?{ ? ?private ?Messenger ?messenger; ? ?public ?MessengerDecorator(Messenger ?messenger) ?{ ? ? ? ?this.messenger ?= ?messenger; ? ?} ? ?public ?String ?getMessage() ?{ ? ? ? ?return ?messenger.getMessage().toUpperCase(); ? ?}}
因?yàn)镸essengerDecorator實(shí)現(xiàn)了Messenger,Util.broadcast()將接受一個(gè)MessengerDecorator的實(shí)例。然而,MessengerDecorator不僅僅是一個(gè)接口的實(shí)現(xiàn),它還是一個(gè)MessengerImpl對象的裝飾器。正因如此,MessengerDecorator就必須有一個(gè)接收擬被裝飾的Messenger對象的構(gòu)造方法。
如列表5所示,這個(gè)構(gòu)造方法將參數(shù)傳給變量。你現(xiàn)在可以覆蓋MessengerDecorator中的getMessage()方法,以便將消息轉(zhuǎn)為大寫后再打印出來。因?yàn)槟愠钟性瓉鞰essenger對象的引用,你可以這樣寫getMessage()方法:
public ?String ?getMessage() ?{ ? ?return ?this.messenger.getMessage().toUpperCase();}
MessengerDecorator中的getMessage()方法返回原始消息的大寫版本。
在你的類中,就像往常一樣,你得到一個(gè)Messenger對象,并將Decorator傳給Util.broadcast()。
Messenger ?messenger ?= ?factory.getMessenger();Util.broadcast(new ?MessengerDecorator(messenger));
你并不將原始對象傳給原先的目標(biāo),相反,你將其傳給了該對象的裝飾器。
應(yīng)用裝飾模式于Servlet
以上Messenger類的例子與servlet容器所構(gòu)造的ServletRequest對象是一樣的。當(dāng)收到一個(gè)HTTP請求時(shí),servlet容器就會(huì)創(chuàng)建ServletRequest對象及ServletResponse對象(分別是ServletRequestImpl及ServletResponseImpl的實(shí)例),并將這兩個(gè)對象傳遞給特定的servlet服務(wù)方法。現(xiàn)在,如果你為ServletRequest創(chuàng)建一個(gè)裝飾角色,并將其傳給servlet服務(wù)方法,你就應(yīng)用了裝飾模式。
對ServletRequest很容易應(yīng)用裝飾模式,因?yàn)閟ervlet ?API已經(jīng)為其提供了一個(gè)包裝類:ServletRequestWrapper。圖3是一個(gè)servlet裝飾模式的類圖。

圖3:Servlet ?API中的裝飾模式
圖3中的HTTP版本的類圖如圖4所示。別為過多的類搞暈了頭,只管注意虛線框中的三個(gè)類就行了:HttpServletRequest, ?HttpServletRequestImpl, ?HttpServletRequestWrapper。
?

圖4:Servlet ?API ?(HTTP)的裝飾模式
情況與前面所舉例子類似。你擁有一個(gè)ServletRequest的實(shí)現(xiàn),而它是由servlet容器產(chǎn)生的。你可以使用所提供的ServletRequestWrapper來裝飾這些ServletRequest對象。
這個(gè)模式很簡單,在實(shí)際應(yīng)用中可以派上用場。實(shí)際上,一些很有名的應(yīng)用就使用了此模式。這些應(yīng)用包括:
? ? ? ? ? ? ? ?Struts ?- ?Struts是當(dāng)前開發(fā)Java ?Web應(yīng)用最受歡迎的基于MVC(模型-視圖-控制)模式的框架。Struts提供了相當(dāng)于ServletRequest包裝類的org.apache.struts.upload.MultipartRequestWrapper類。 ?MultipartRequestWrapper覆蓋了getParameter(),getParameterNames(),及getParameterValues()等方法來實(shí)現(xiàn)文件上傳。
? ? ? ? ? ? ? ?Apache ?Beehive ??C ?這個(gè)源于BEA的WebLogic專題小組的開源項(xiàng)目,構(gòu)建于Struts之上,并簡化了web應(yīng)用及web服務(wù)的開發(fā)。與ServletRequest包裝類一樣,org.apache.beehive.netui.pageflow.internal包中的PageFlowRequestWrapper類有助于任意Apache ?Beehive應(yīng)用的頁流處理。
現(xiàn)在,讓我們來看看,如何編寫自己的HttpServletRequest裝飾類。
一個(gè)刪除空白字符的Filter
本節(jié)將以上的理論投入實(shí)際使用,通過實(shí)現(xiàn)一個(gè)刪除空白字符的filter,來演示如何使用javax.servlet.http.HttpServletRequestWrapper類來裝飾HttpServletRequest對象。在本例中,這個(gè)filter將刪除所傳來的參數(shù)中多余的空白字符。
這在許多servlet/JSP應(yīng)用中是很有用的,包括Struts及JavaServer ?Faces等應(yīng)用。例如,Struts通過調(diào)用HttpServletRequest對象的getParameterValues()對象來處理action表單。通過覆蓋裝飾類中此方法,你可以改變當(dāng)前HttpServletRequest對象的狀態(tài)。
要?jiǎng)?chuàng)建HttpServletRequest的裝飾類,你需要繼承HttpServletRequestWrapper并且覆蓋你希望改變的方法。列表5中,MyRequestWrapper類將刪除getParameterValues()方法返回值的多余空白字符。
列表5:HttpServerletRequest裝飾類
列表6演示了如何載獲Http請求并裝飾HttpServletRequest對象。[i]列表6:刪除空白符的filter
列表6演示了如何載獲Http請求并裝飾HttpServletRequest對象。
[i]列表6:刪除空白符的filter
這個(gè)程序使用了列表6所示的filter來修整用戶輸入。要使用這個(gè)filter,你需要在web.xml文件中如下設(shè)置filter及filter-mapping的元素。
? ?
?<filter>
? ? ? ?<filter-name>TrimmerFilter</filter-name>
? ? ? ?<filter-class>trimmer.filter.MyFilter</filter-class>
? ?</filter>
? ?<filter-mapping>
? ? ? ?<filter-name>TrimmerFilter</filter-name>
? ? ? ?<url-pattern>*.do</url-pattern>
? ?</filter-mapping>
要測試這個(gè)filter,啟動(dòng)這個(gè)應(yīng)用后,在表單中輸入一些值,提交表單,看看這個(gè)filter是如何修整輸入數(shù)值的。這是一個(gè)實(shí)用的裝飾模式的應(yīng)用。
小結(jié)
Servlet ?filter可以在調(diào)用一個(gè)servlet的服務(wù)方法后,攔載或加工HTTP請求。盡管這非常誘人,但其實(shí)際使用卻有所限制,因?yàn)槟悴荒芨淖僅ttpServletRequest對象。
這時(shí)候裝飾模式派上了用場。本文演示了如何通過應(yīng)用裝飾模式來“修改”HttpServletRequest對象,從而使你的servlet ?filter更加有用。在上面filter例子中,filter改了request參數(shù)中的用戶輸入,而這一點(diǎn),如果沒有裝飾request對象,你是無論如何也不可能做到的。
Budi ?Kurniawan是一個(gè)高級J2EE的架構(gòu)師。他還是《Tomcat如何工作:教你如何開發(fā)自己的Servlet容器》 ?(”How ?Tomcat ?Works: ?A ?Guide ?to ?Developing ?Your ?Own ?Servlet ?Container”) ?以及《Struts設(shè)計(jì)與編程指南》(”Struts ?Design ?and ?Programming ?:A ?Tutorial”) ?這兩本書的作者,它們均由BrainySoftwar.com出版。
英文原文地址:
http://dev2dev.bea.com/pub/a/2005/05/decorators.html
中文地址
http://www.matrix.org.cn/resource/article/43/43603_Servlet_Request.html
關(guān)鍵詞:Servlet ?Request ? ?filter ?Decorator
摘要
裝飾模式是Erich ?Gamma等人所著的《設(shè)計(jì)模式:可利用面向?qū)ο筌浖幕A(chǔ)》一書中眾多模式之一。一般來說,此模式在設(shè)計(jì)Swing的程序員中比較流行,他們用它來改進(jìn)軟件。今天,即使有許多程序是基于Web應(yīng)用的,裝飾模式仍有用武之地,在J2EE的環(huán)境下也有使用的價(jià)值。
本文說明了如何將裝飾模式應(yīng)用到servlet ?request對象上。首先,提出了一個(gè)與servlet ?filter有關(guān)的問題,并解釋了隨之而引入的裝飾模式。然后,討論了如何在servlet環(huán)境下使用此模式,并列出了使用此模式的幾個(gè)比較有名的基于servlet的項(xiàng)目。最后,文章通過實(shí)現(xiàn)一個(gè)刪除空白符的filter例子,演示了裝飾模式在servlet中的使用。
簡介
? ? ? ?Servlet規(guī)范中所引入的filter令人心動(dòng)不已,因?yàn)樗肓艘粋€(gè)功能強(qiáng)大的攔截模式。Filter是這樣一種Java對象,它能在request到達(dá)servlet的服務(wù)方法之前攔截HttpServletRequest對象,而在服務(wù)方法轉(zhuǎn)移控制后又能攔截HttpServletResponse對象。你可以使用filter來實(shí)現(xiàn)特定的任務(wù),比如驗(yàn)證用戶輸入,以及壓縮web內(nèi)容。但你擬富有成效地使用過濾器的念頭卻被你不能改變HttpServletRequest對象的參數(shù)的現(xiàn)實(shí)掃了興,因?yàn)閖ava.util.Map所包裝的HttpServletRequest對象的參數(shù)是不可改變的。這極大地縮減了filter的應(yīng)用范圍。至少在一半的時(shí)間里,你希望可以改變準(zhǔn)備傳送給filter的對象。如果在HttpServletRequest對象到達(dá)Struts的action ?servlet之前,我們可以通過一個(gè)filter將用戶輸入的多余空格去掉,難道不是更美妙嗎?這樣的話,你就不必等到在Struts的action表單驗(yàn)證方法中才進(jìn)行這項(xiàng)工作了。
幸運(yùn)的是,盡管你不能改變不變對象本身,但你卻可以通過使用裝飾模式來改變其狀態(tài)。
裝飾模式
在繼承中,你可以通過繼承一個(gè)父類并覆蓋你希望改變的方法來改變對象狀態(tài)。然而,如果這個(gè)對象是由程序的另一個(gè)子模塊,例如對象工廠 ?(這里所說的工廠是工廠模式中的術(shù)語,下同。譯者注) ?或是servlet容器所產(chǎn)生的,繼承就無能為力了。
裝飾模式可用來增加一個(gè)現(xiàn)有對象的功能,或是改變其狀態(tài)。與其使用繼承方式來擴(kuò)展此類,這個(gè)模式將一個(gè)對象包裝成另外一個(gè)對象。圖1是裝飾模式的UML類圖。

圖1:裝飾模式
在圖1中,Component是一個(gè)接口,其具體實(shí)現(xiàn)是ConcreteComponent。要改變Component的狀態(tài),你可以修改ConcreteComponent或是擴(kuò)展它 ?(通過繼承或?qū)崿F(xiàn)接口的方式,譯者注)。然而,如果ConcreteComponent來自于一個(gè)工廠,你卻無計(jì)可施。你所能做的,就是創(chuàng)建一個(gè)同為實(shí)現(xiàn)了Component接口的裝飾類。在圖1中,這個(gè)裝飾類的角色就由Decorator來扮演,在程序中通常表現(xiàn)為接口或抽象類。Decorator類的一個(gè)特性就是,它有一個(gè)接收Component對象的構(gòu)造方法。你將擬裝飾的對象傳遞給這個(gè)構(gòu)造方法。在本例中,這個(gè)對象就是從工廠獲得的ConcreteComponent對象。通過將此裝飾對象傳遞給Decorator的一個(gè)類變量,你可以訪問Decorator中的任何方法。這就使你得以改變對象的狀態(tài)了。
圖1中的Decorator類不一定是接口或抽象類。如果你的程序不是很復(fù)雜,你可以將其轉(zhuǎn)化為一個(gè)具體的Decorator類。
舉個(gè)例子,考慮這樣一個(gè)簡單的消息傳遞程序,其主要部分是Messenger接口及其實(shí)現(xiàn)類MessengerImpl。讓我們假設(shè)MessengerImpl對象來自于一個(gè)工廠,因此你不能改變其狀態(tài)。如果你準(zhǔn)備增加或改變Messenger對象的功能,你可以創(chuàng)建一個(gè)MessengerDecorator類。圖2是此例子的類圖。

圖2:Messenger裝飾類
我們來看程序的代碼。列表1給出了Messenger接口的代碼,列表2是MessengerImpl類的代碼。
列表1:Messenger接口
public ?interface ?Messenger ?{ ? ?public ?String ?getMessage();}
列表2:MessengerImpl類
程序代碼: |
public?class?MessengerImpl?implements?Messenger?{??private?String?message;??public?MessengerImpl(String?message)?{????this.message?=?message;??}??public?String?getMessage()?{????return?message;??}} |
Messenger對象由一個(gè)名為MessengerFactory的工廠創(chuàng)建,如列表3所示。
列表3:MessengerFactory類
public ?class ?MessengerFactory ?{ ? ?public ?static ?Messenger ?getMessenger() ?{ ? ? ? ?return ?new ?MessengerImpl("secrets"); ? ?}}
對每一個(gè)所創(chuàng)建的Messenger對象,此工廠通過某個(gè)未知的操作,初始化了getMessage()方法所返回的字符串。換句話說,你不能自己創(chuàng)建Messenger對象。
在程序中,Messenger對象的主要用途是被傳遞給一個(gè)名為Util的類中的broadcast()靜態(tài)方法。列表4是Util類的代碼。
列表4:Util類
public ?class ?Util ?{ ? ?public ?static ?void ?broadcast(Messenger ?messenger) ?{ ? ? ? ?System.out.print(messenger.getMessage()); ? ?} ? ?// ?other ?methods ?here}
在你自己的類中,你可能會(huì)有這樣的代碼:
Messenger ?messenger ?= ?MessengerFactory.getMessenger();Util.broadcast(messenger);
假設(shè)你希望對broadcast()方法所打印出的消息做一小改動(dòng)。你擬將其轉(zhuǎn)為大寫,怎么做?表面上看,你可以繼承Messenger,實(shí)例化其子類,并將返回的對象傳給Util.broadcast()。但是,這種做法毫無意義,因?yàn)橹挥泄S才知道如何初始化Messenger對象,并通過其getMessage()方法返回正確的值。
使用裝飾模式,你可以創(chuàng)建一個(gè)MessengerDecorator類,如列表5所示。
列表5:MessengerDecorator類
public ?class ?MessengerDecorator ?implements ?Messenger ?{ ? ?private ?Messenger ?messenger; ? ?public ?MessengerDecorator(Messenger ?messenger) ?{ ? ? ? ?this.messenger ?= ?messenger; ? ?} ? ?public ?String ?getMessage() ?{ ? ? ? ?return ?messenger.getMessage().toUpperCase(); ? ?}}
因?yàn)镸essengerDecorator實(shí)現(xiàn)了Messenger,Util.broadcast()將接受一個(gè)MessengerDecorator的實(shí)例。然而,MessengerDecorator不僅僅是一個(gè)接口的實(shí)現(xiàn),它還是一個(gè)MessengerImpl對象的裝飾器。正因如此,MessengerDecorator就必須有一個(gè)接收擬被裝飾的Messenger對象的構(gòu)造方法。
如列表5所示,這個(gè)構(gòu)造方法將參數(shù)傳給變量。你現(xiàn)在可以覆蓋MessengerDecorator中的getMessage()方法,以便將消息轉(zhuǎn)為大寫后再打印出來。因?yàn)槟愠钟性瓉鞰essenger對象的引用,你可以這樣寫getMessage()方法:
public ?String ?getMessage() ?{ ? ?return ?this.messenger.getMessage().toUpperCase();}
MessengerDecorator中的getMessage()方法返回原始消息的大寫版本。
在你的類中,就像往常一樣,你得到一個(gè)Messenger對象,并將Decorator傳給Util.broadcast()。
Messenger ?messenger ?= ?factory.getMessenger();Util.broadcast(new ?MessengerDecorator(messenger));
你并不將原始對象傳給原先的目標(biāo),相反,你將其傳給了該對象的裝飾器。
應(yīng)用裝飾模式于Servlet
以上Messenger類的例子與servlet容器所構(gòu)造的ServletRequest對象是一樣的。當(dāng)收到一個(gè)HTTP請求時(shí),servlet容器就會(huì)創(chuàng)建ServletRequest對象及ServletResponse對象(分別是ServletRequestImpl及ServletResponseImpl的實(shí)例),并將這兩個(gè)對象傳遞給特定的servlet服務(wù)方法。現(xiàn)在,如果你為ServletRequest創(chuàng)建一個(gè)裝飾角色,并將其傳給servlet服務(wù)方法,你就應(yīng)用了裝飾模式。
對ServletRequest很容易應(yīng)用裝飾模式,因?yàn)閟ervlet ?API已經(jīng)為其提供了一個(gè)包裝類:ServletRequestWrapper。圖3是一個(gè)servlet裝飾模式的類圖。

圖3:Servlet ?API中的裝飾模式
圖3中的HTTP版本的類圖如圖4所示。別為過多的類搞暈了頭,只管注意虛線框中的三個(gè)類就行了:HttpServletRequest, ?HttpServletRequestImpl, ?HttpServletRequestWrapper。
?

圖4:Servlet ?API ?(HTTP)的裝飾模式
情況與前面所舉例子類似。你擁有一個(gè)ServletRequest的實(shí)現(xiàn),而它是由servlet容器產(chǎn)生的。你可以使用所提供的ServletRequestWrapper來裝飾這些ServletRequest對象。
這個(gè)模式很簡單,在實(shí)際應(yīng)用中可以派上用場。實(shí)際上,一些很有名的應(yīng)用就使用了此模式。這些應(yīng)用包括:
? ? ? ? ? ? ? ?Struts ?- ?Struts是當(dāng)前開發(fā)Java ?Web應(yīng)用最受歡迎的基于MVC(模型-視圖-控制)模式的框架。Struts提供了相當(dāng)于ServletRequest包裝類的org.apache.struts.upload.MultipartRequestWrapper類。 ?MultipartRequestWrapper覆蓋了getParameter(),getParameterNames(),及getParameterValues()等方法來實(shí)現(xiàn)文件上傳。
? ? ? ? ? ? ? ?Apache ?Beehive ??C ?這個(gè)源于BEA的WebLogic專題小組的開源項(xiàng)目,構(gòu)建于Struts之上,并簡化了web應(yīng)用及web服務(wù)的開發(fā)。與ServletRequest包裝類一樣,org.apache.beehive.netui.pageflow.internal包中的PageFlowRequestWrapper類有助于任意Apache ?Beehive應(yīng)用的頁流處理。
現(xiàn)在,讓我們來看看,如何編寫自己的HttpServletRequest裝飾類。
一個(gè)刪除空白字符的Filter
本節(jié)將以上的理論投入實(shí)際使用,通過實(shí)現(xiàn)一個(gè)刪除空白字符的filter,來演示如何使用javax.servlet.http.HttpServletRequestWrapper類來裝飾HttpServletRequest對象。在本例中,這個(gè)filter將刪除所傳來的參數(shù)中多余的空白字符。
這在許多servlet/JSP應(yīng)用中是很有用的,包括Struts及JavaServer ?Faces等應(yīng)用。例如,Struts通過調(diào)用HttpServletRequest對象的getParameterValues()對象來處理action表單。通過覆蓋裝飾類中此方法,你可以改變當(dāng)前HttpServletRequest對象的狀態(tài)。
要?jiǎng)?chuàng)建HttpServletRequest的裝飾類,你需要繼承HttpServletRequestWrapper并且覆蓋你希望改變的方法。列表5中,MyRequestWrapper類將刪除getParameterValues()方法返回值的多余空白字符。
列表5:HttpServerletRequest裝飾類
程序代碼: |
package?trimmer.filter; import?javax.servlet.http.HttpServletRequest; import?javax.servlet.http.HttpServletRequestWrapper; public?final?class?MyRequestWrapper?extends?HttpServletRequestWrapper?{ public?MyRequestWrapper(HttpServletRequest?servletRequest)?{ super(servletRequest); } public?String[]?getParameterValues(String?parameter)?{ String[]?results?=?super.getParameterValues(parameter); if?(results?==?null) return?null; int?count?=?results.length; String[]?trimResults?=?new?String[count]; for?(int?i?=?0;?i?<?count;?i++)?{ trimResults[i]?=?results[i].trim(); } return?trimResults; } } |
列表6演示了如何載獲Http請求并裝飾HttpServletRequest對象。[i]列表6:刪除空白符的filter
列表6演示了如何載獲Http請求并裝飾HttpServletRequest對象。
[i]列表6:刪除空白符的filter
程序代碼: |
package?trimmer.filter; import?java.io.IOException; import?javax.servlet.Filter; import?javax.servlet.FilterChain; import?javax.servlet.FilterConfig; import?javax.servlet.ServletException; import?javax.servlet.ServletRequest; import?javax.servlet.ServletResponse; import?javax.servlet.http.HttpServletRequest; public?class?MyFilter?implements?Filter?{ private?FilterConfig?filterConfig; public?void?init(FilterConfig?filterConfig)?throws?ServletException?{ System.out.println("Filter?initialized"); this.filterConfig?=?filterConfig; } public?void?destroy()?{ System.out.println("Filter?destroyed"); this.filterConfig?=?null; } public?void?doFilter(ServletRequest?request,?ServletResponse?response, FilterChain?chain)?throws?IOException,?ServletException?{ chain.doFilter(new?MyRequestWrapper((HttpServletRequest)?request), response); } } |
這個(gè)程序使用了列表6所示的filter來修整用戶輸入。要使用這個(gè)filter,你需要在web.xml文件中如下設(shè)置filter及filter-mapping的元素。
? ?
?<filter>
? ? ? ?<filter-name>TrimmerFilter</filter-name>
? ? ? ?<filter-class>trimmer.filter.MyFilter</filter-class>
? ?</filter>
? ?<filter-mapping>
? ? ? ?<filter-name>TrimmerFilter</filter-name>
? ? ? ?<url-pattern>*.do</url-pattern>
? ?</filter-mapping>
要測試這個(gè)filter,啟動(dòng)這個(gè)應(yīng)用后,在表單中輸入一些值,提交表單,看看這個(gè)filter是如何修整輸入數(shù)值的。這是一個(gè)實(shí)用的裝飾模式的應(yīng)用。
小結(jié)
Servlet ?filter可以在調(diào)用一個(gè)servlet的服務(wù)方法后,攔載或加工HTTP請求。盡管這非常誘人,但其實(shí)際使用卻有所限制,因?yàn)槟悴荒芨淖僅ttpServletRequest對象。
這時(shí)候裝飾模式派上了用場。本文演示了如何通過應(yīng)用裝飾模式來“修改”HttpServletRequest對象,從而使你的servlet ?filter更加有用。在上面filter例子中,filter改了request參數(shù)中的用戶輸入,而這一點(diǎn),如果沒有裝飾request對象,你是無論如何也不可能做到的。
Budi ?Kurniawan是一個(gè)高級J2EE的架構(gòu)師。他還是《Tomcat如何工作:教你如何開發(fā)自己的Servlet容器》 ?(”How ?Tomcat ?Works: ?A ?Guide ?to ?Developing ?Your ?Own ?Servlet ?Container”) ?以及《Struts設(shè)計(jì)與編程指南》(”Struts ?Design ?and ?Programming ?:A ?Tutorial”) ?這兩本書的作者,它們均由BrainySoftwar.com出版。