| 簡(jiǎn)介: 我看到很多項目中,開(kāi)發(fā)者實(shí)現了自己的MVC框架,并不是因為他們想做同Struts根本不同的東西,而是因為他們并沒(méi)有意識到如何擴展Struts。開(kāi)發(fā)自己的MVC框架可以獲得全部的控制權,但是這也意味著(zhù)需要很多資源來(lái)實(shí)現它(人力物力),在緊張的日程安排下,有時(shí)候這是不可能的。 Struts不僅僅是一個(gè)強大的框架,同時(shí)它也是可擴展的。你可以以三種方式來(lái)擴展Struts。 1, PlugIn:如果你想在application startup或shutdown的時(shí)候做一些業(yè)務(wù)邏輯的話(huà),那就創(chuàng )建你自己的PlugIn類(lèi)。 2, RequestProcessor:如果你想在請求被處理的過(guò)程中某個(gè)時(shí)刻做一些業(yè)務(wù)邏輯的話(huà),那么創(chuàng )建你自己的RequestProcessor類(lèi)。比如說(shuō),在每次請求執行之前,你可以擴展RequestProcessor來(lái)檢查用戶(hù)是否登陸了以及他是否有權限去執行某個(gè)特定的action。 3, ActionServlet:如果你想在application startup和shutdown的時(shí)候以及請求被處理的時(shí)候做某些業(yè)務(wù)邏輯,你也可以擴張ActionServlet類(lèi)。不過(guò)你應當在PlugIn和RequestProcessor都不能解決你的需求的時(shí)候來(lái)使用ActionServlet。 在這篇文章中,我們將使用一個(gè)Struts應用的示例來(lái)示范如何使用這三種方式來(lái)擴展Struts。示例程序的代碼可以從 ![]() 我們假設你已經(jīng)比較熟悉Struts框架并且知道如何使用它創(chuàng )建一個(gè)簡(jiǎn)單的應用。如果你想知道更多關(guān)于Struts的內容,請參考官方主頁(yè)。 PlugIn PlugIn是一個(gè)接口,你可以創(chuàng )建一個(gè)實(shí)現該接口的類(lèi),當application startup或shutdown的時(shí)候做些事情。 比方說(shuō),我創(chuàng )建了一個(gè)使用Hibernate作為持久層的web應用,我想當應用啟動(dòng)的時(shí)候就初始化Hibernate,這樣子當我的web應用受到第一個(gè)請求的時(shí)候,Hibernate就已經(jīng)是配置好的并且可用的。同時(shí)我們想當application關(guān)閉的時(shí)候關(guān)閉Hibernate。我們可以用一個(gè)Hibernate PlugIn來(lái)實(shí)現這個(gè)需求,通過(guò)如下的兩步: 1, 創(chuàng )建一個(gè)類(lèi)實(shí)現了PlugIn接口:
實(shí)現PlugIn接口的類(lèi)必須完成兩個(gè)方法:init()和destroy()。當application startup的時(shí)候init()方法被調用,當shutdown的時(shí)候destroy()方法被調用。Struts還允許給你的PlugIn類(lèi)傳遞初始化參數。為了傳遞參數,你必須在PlugIn類(lèi)中為每一個(gè)參數創(chuàng )建JavaBean式的setter方法。在我們的HibernatePlugIn類(lèi)中,我會(huì )把configFile的name作為參數傳進(jìn)去,而不是硬編碼到程序中。 2, 在struts-config.xml中添加如下的代碼來(lái)通告Struts有新的PlugIn:
屬性className是實(shí)現了PlugIn接口的類(lèi)的全限定名。對于每一個(gè)初始化參數,可以使用<set-property>元素傳遞參數。在我們的例子中,我要把config文件的名字傳進(jìn)去,所以使用了一個(gè)帶有配置文件路徑的<set-property>。 Struts的Tiles和Validator框架都使用PlugIn來(lái)讀取配置文件進(jìn)行初始化。另外兩件PlugIn可以幫你做到的事情是: l 如果你的application依賴(lài)于某些配置文件,那么你可以在PlugIn類(lèi)中檢查它們是否可用,如果不可用的話(huà)拋出一個(gè)ServletException,這樣就可以使ActionServlet變?yōu)椴豢捎谩?br>l PlugIn接口的init()方法是你可以改變ModuleConfig的最后機會(huì ),ModuleConfig是一組靜態(tài)配置信息的集合,用來(lái)描述基于Struts模塊。Struts將會(huì )在所有PlugIn處理完后釋放ModuleConfig。 Request是如何被處理的 ActionServlet是Struts框架中唯一的Servlet,它負責處理所有request。無(wú)論何時(shí)接收到一個(gè)request,它都會(huì )先嘗試為當前的request尋找一個(gè)sub-application。一旦一個(gè)sub-application被找到,ActionServlet就會(huì )為那個(gè)sub-application創(chuàng )建一個(gè)RequestProcessor對象,調用這個(gè)對象的process()方法并把HttpServletRequest和HttpServletResponse對象傳入。 RequestProcessor.process()就是大部分request被處理的地方。process()方法使用了Template Method模式實(shí)現,其中有很多獨立的方法來(lái)執行請求處理的每一步驟,這些方法將會(huì )在process方法中依次被調用。比如,將會(huì )有一個(gè)獨立的方法用來(lái)尋找當前request對應的ActionForm類(lèi),一個(gè)方法來(lái)檢查當前用戶(hù)是否有執行action mapping所必須的權限。這些給與我們極大的靈活性。在發(fā)布的Struts包中有一個(gè)RequestProcessor類(lèi)提供了請求處理每一步驟的默認實(shí)現。這就意味著(zhù)你可以?xún)H僅重寫(xiě)你感興趣的方法,其它的使用默認的實(shí)現。舉例來(lái)說(shuō),默認地Struts調用request.isUserInRole()來(lái)檢查用戶(hù)是否有權限執行當前的ActionMapping;這時(shí)如果你想通過(guò)查詢(xún)數據庫來(lái)實(shí)現,你所要做的就是重寫(xiě)processRoles()方法,通過(guò)查詢(xún)出的用戶(hù)是否擁有必須的權限來(lái)返回true或false。 首先我們將會(huì )看到缺省情況下,process()方法是如何實(shí)現的,然后我將會(huì )詳細解釋默認的RequestProcessor類(lèi)中的每一個(gè)方法,這樣你就可以決定哪一部分是你想要改變的。
1, processMutipart():在這個(gè)方法中,Struts將會(huì )讀取request來(lái)檢查request的contentType是否是multipart/form-data。如果是的話(huà),將會(huì )解析request并且將之包裝到HttpServletRequest中。當你創(chuàng )建了一個(gè)HTML FORM用來(lái)提交數據,那么request的contentType默認就是application/x-www-form-urlencoded。但是如果你的form使用了file類(lèi)型的input控件允許用戶(hù)上傳文件的話(huà),你就必須將contentType改為multipart/form-data。如果是這樣的情況,你就不能再通過(guò)getParameter()來(lái)獲取用戶(hù)提交的數據;你必須將request作為一個(gè)InputStream來(lái)讀取,并且自己解析它來(lái)獲得參數值。 2, processPath():在這個(gè)方法中,Struts將會(huì )讀取request的URI,來(lái)確定路徑元素,這個(gè)元素是用來(lái)獲取ActionMappint元素。 3, processLocale():在這個(gè)方法中,Struts將會(huì )為當前request取得Locale,如果配置過(guò)的話(huà),還可以將這個(gè)對象作為HttpSession中org.apache.struts.action.LOCALE屬性的值而保存。作為這個(gè)方法的副作用,HttpSession將會(huì )被創(chuàng )建,如果你不想創(chuàng )建的話(huà),你可以在ControllerConfig中將locale屬性設為false,在struts-config.xml中象如下這樣:
4, processContent():通過(guò)調用response.setContentType()來(lái)為response設置contentType。這個(gè)方法首先會(huì )嘗試從struts-config.xml配置中得到contentType。缺省情況下使用text/html。如果想覆蓋它,可以象如下這樣:
5, processNoCache():如果配置是no-cache,Struts將會(huì )為每個(gè)response設置下面三個(gè)headers:
如果你想設置no-cache header,在struts-config.xml中加入下面信息:
6, processPreprocess():這個(gè)方法為預處理提供一個(gè)hook,可以在子類(lèi)中覆蓋它。它的缺省實(shí)現沒(méi)有作任何事情,總是返回true。返回false的話(huà)將會(huì )終止當前請求的處理。 7, processMapping():這個(gè)方法將會(huì )用路徑信息得到一個(gè)ActionMapping對象。也就是struts-config.xml文件中的<action>元素:
ActionMapping元素包含了Action類(lèi)的名稱(chēng)和處理請求使用的ActionForm等等信息。它還包含當前ActionMapping配置的ActionForwards信息。 8, processRoles():Struts web應用提供了一個(gè)授權方案。也就是說(shuō),一旦一個(gè)用戶(hù)登入了容器,struts的processRoles()方法將會(huì )通過(guò)調用request.isUserInRole(),來(lái)檢查他是否有必須的角色來(lái)運行一個(gè)給定的ActionMapping。
假設你有一個(gè)AddUserAction并且你只想讓administrator能夠增加新的user。你所要做的就是給你的AddUserAction元素增加一個(gè)role屬性,這個(gè)屬性的值為administrator。這樣,在運行AddUserAction之前,這個(gè)方法會(huì )確保用戶(hù)擁有administraotr的角色。 9, processActionForm():每一個(gè)ActionMapping都一個(gè)相應的ActionForm類(lèi)。當Struts處理一個(gè)ActionMapping的時(shí)候,它將會(huì )從<action>元素的name屬性中找出對應的ActionForm類(lèi)的名稱(chēng)。
在我們的例子中,它會(huì )先在request scope中檢查是否有一個(gè)org.apache.struts.action.DynaActionForm類(lèi)的對象存在。如果有它將會(huì )使用這個(gè)對象,如果沒(méi)有它將會(huì )創(chuàng )建一個(gè)新的對象并把它設置在request scope。 10, processPopulate():在這個(gè)方法中,Struts將會(huì )用相匹配的request參數裝配ActionForm的實(shí)例變量。 11, processValidate():Struts將會(huì )調用你的ActionForm類(lèi)的validate方法。如果你從validate()返回ActionErrors,它將會(huì )將user重定向到<action>元素的input屬性指定的頁(yè)面。 12, processForward()和processInclude():在這些方法中,Struts將會(huì )檢查<action>元素的forward或include屬性,如果找到了,將會(huì )把forward或include請求放置到配置的頁(yè)面中。
你可以從這些方法的名字上猜測它們的不同:processForward()最終調用RequestDispatcher.forward(),而processInclude()調用RequestDispatcher.include()。如果你同時(shí)配置了forward和include屬性,它將會(huì )總是調用forward,因為forward先被處理。 13, processActionCreate():這個(gè)方法從<action>元素的type屬性中獲取獲得Action類(lèi)的名字并且創(chuàng )建返回它的實(shí)例。在我們的例子中,它將會(huì )創(chuàng )建一個(gè)com.sample.NewContactAction類(lèi)的實(shí)例。 14, processActionPerform():這個(gè)方法調用你的Action類(lèi)的excute()方法,你的業(yè)務(wù)邏輯也就是在excute方法中。 15, processForwardConfig():你的Action類(lèi)的excute()方法將會(huì )返回一個(gè)ActionForward對象,這個(gè)對象將指出哪個(gè)頁(yè)面是顯示給用戶(hù)的頁(yè)面。因此,Struts將會(huì )為那個(gè)頁(yè)面創(chuàng )建一個(gè)RequestDispatcher,并且調用RequestDispatcher.forward()。 上面的列表說(shuō)明了默認的RequestProcessor實(shí)現在處理請求時(shí)每一步作的工作,以及執行的順序。正如你所看到的,RequestProcessor是非常靈活的,允許你通過(guò)設置<controller>元素的屬性來(lái)配置它。舉例來(lái)說(shuō),如果你的應用準備生成XML內容來(lái)代替HTML,你就可以通過(guò)設置controller元素的屬性來(lái)通知Struts這些情況。 創(chuàng )建你自己的RequestProcessor 通過(guò)上面,我們了解到了RequestProcessor的默認實(shí)現是如何工作的?,F在我們要演示一個(gè)例子來(lái)說(shuō)明如何定制你自己的RequestProcessor。為了展示創(chuàng )建用戶(hù)定制的RequestProcessor,我們將會(huì )讓我們的示例實(shí)現下面兩個(gè)業(yè)務(wù)需求: l 我們想創(chuàng )建一個(gè)ContactImageAction類(lèi),它將生成圖片而不是平常的HTML頁(yè)面。 l 在每個(gè)請求處理之前,我們都想通過(guò)檢查session中的userName屬性來(lái)確定用戶(hù)是否已經(jīng)登陸。如果那個(gè)屬性沒(méi)有找到,我們會(huì )把用戶(hù)重定向到登陸頁(yè)面。 我們將分兩步實(shí)現這些業(yè)務(wù)需求。 1, 創(chuàng )建你的CustomRequestProcessor類(lèi),它將繼承自RequestProcessor類(lèi),如下:
在CustomRequestProcessor類(lèi)的processPreprocess方法中,我們檢查session的userName屬性,如果沒(méi)有找到,就將用戶(hù)重定向到登陸頁(yè)面。 對于生成圖片作為輸出的需求,我們必須覆蓋processContent方法,首先檢查請求是否是/contactimage路徑。如果是的話(huà),我們就會(huì )將contentType設置為image/gif;否則設置為text/html。 2, 在你的struts-config.xml文件的<action-mappint>元素之后加入下面的文字,告訴Struts CustomRequestProcessor應當被用作RequestProcessor類(lèi):
請注意,當你只有很少的action類(lèi)需要生成非text/html類(lèi)型的輸出時(shí),你覆寫(xiě)processContent()方法是OK的。如果不是這樣子的話(huà),你應該創(chuàng )建一個(gè)Struts的子應用來(lái)處理請求生成圖片的Action,并為它們將contentType設置為image/gif。 Struts的Tiles框架就是使用它自己的RequestProcessor來(lái)裝飾Struts的輸出。 ActionServlet 如果你查看你的Struts web應用的web.xml,你會(huì )看到這樣的文字:
這意味著(zhù)ActionServlet負責處理你所有Struts的請求。你可以創(chuàng )建一個(gè)ActionServlet的子類(lèi),當應用啟動(dòng),關(guān)閉,每個(gè)請求的時(shí)候做一些特定的事情。但是在繼承ActionServlet類(lèi)之前,你應該盡量創(chuàng )建一個(gè)PlugIn或RequestProcessor去解決你的問(wèn)題。在Servlet1.1之前,Tiles框架是基于A(yíng)ctionServlet來(lái)修飾生成的響應。但是從1.1之后,它開(kāi)始使用TilesRequestProcessor類(lèi)。 總結 決定開(kāi)發(fā)你自己的MVC框架是一個(gè)非常大的決定,你必須要考慮開(kāi)發(fā)和維護框架代碼所花費的時(shí)間和資源。Struts是一個(gè)非常強大和穩定的框架,你可以修改它來(lái)滿(mǎn)足你絕大多數的業(yè)務(wù)需求。 但另一方面,也不要草率地做出擴展Struts的決定。如果你在RequestProcessor中寫(xiě)了一些性能比較低的代碼,它將會(huì )在每次請求時(shí)執行,因而降低你整個(gè)應用的效率。而且還是有一些情況,開(kāi)發(fā)自己的MVC框架要比擴展Struts好。 | ||||||||||||||
參與論壇討論:http://www.matrix.org.cn/forum.asp 更多技術(shù)文章:http://www.matrix.org.cn/article.asp Matrix java門(mén)戶(hù):http://www.matrix.org.cn | ||||||||||||||
| 原文地址:http://www.matrix.org.cn/article/1100.html |
聯(lián)系客服