美麗涵涵童裝店--說(shuō)我博客名字,給你們打折!
          隨筆 - 82  文章 - 266  trackbacks - 0
          <2007年8月>
          2930311234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678


          點(diǎn)擊這里給楊?lèi)?ài)友發(fā)消息
          美麗涵涵童裝店
          說(shuō)我博客名字,給你們打折!

          常用鏈接

          留言簿(6)

          隨筆分類(lèi)

          隨筆檔案

          文章檔案

          好友的BLOG

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          簡(jiǎn)介:

            我看到很多項(xiàng)目中,開(kāi)發(fā)者實(shí)現(xiàn)了自己的MVC框架,并不是因?yàn)樗麄兿胱鐾琒truts根本不同的東西,而是因?yàn)樗麄儾](méi)有意識(shí)到如何擴(kuò)展Struts。開(kāi)發(fā)自己的MVC框架可以獲得全部的控制權(quán),但是這也意味著需要很多資源來(lái)實(shí)現(xiàn)它(人力物力),在緊張的日程安排下,有時(shí)候這是不可能的。

            Struts不僅僅是一個(gè)強(qiáng)大的框架,同時(shí)它也是可擴(kuò)展的。你可以以三種方式來(lái)擴(kuò)展Struts。

            1、PlugIn:如果你想在application startup或shutdown的時(shí)候做一些業(yè)務(wù)邏輯的話,那就創(chuàng)建你自己的PlugIn類(lèi)。

            2、RequestProcessor:如果你想在請(qǐng)求被處理的過(guò)程中某個(gè)時(shí)刻做一些業(yè)務(wù)邏輯的話,那么創(chuàng)建你自己的RequestProcessor類(lèi)。比如說(shuō),在每次請(qǐng)求執(zhí)行之前,你可以擴(kuò)展RequestProcessor來(lái)檢查用戶(hù)是否登陸了以及他是否有權(quán)限去執(zhí)行某個(gè)特定的action。

            3、ActionServlet:如果你想在application startup和shutdown的時(shí)候以及請(qǐng)求被處理的時(shí)候做某些業(yè)務(wù)邏輯,你也可以擴(kuò)張ActionServlet類(lèi)。不過(guò)你應(yīng)當(dāng)在PlugIn和RequestProcessor都不能解決你的需求的時(shí)候來(lái)使用ActionServlet。

            在這篇文章中,我們將使用一個(gè)Struts應(yīng)用的示例來(lái)示范如何使用這三種方式來(lái)擴(kuò)展Struts。示例程序的代碼可以從http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下載。兩個(gè)擴(kuò)展Struts成功的范例是Struts自身的Validation和Tiles框架。

            我們假設(shè)你已經(jīng)比較熟悉Struts框架并且知道如何使用它創(chuàng)建一個(gè)簡(jiǎn)單的應(yīng)用。如果你想知道更多關(guān)于Struts的內(nèi)容,請(qǐng)參考官方主頁(yè)。

            PlugIn
            
            PlugIn是一個(gè)接口,你可以創(chuàng)建一個(gè)實(shí)現(xiàn)該接口的類(lèi),當(dāng)application startup或shutdown的時(shí)候做些事情。

            比方說(shuō),我創(chuàng)建了一個(gè)使用Hibernate作為持久層的web應(yīng)用,我想當(dāng)應(yīng)用啟動(dòng)的時(shí)候就初始化Hibernate,這樣子當(dāng)我的web應(yīng)用受到第一個(gè)請(qǐng)求的時(shí)候,Hibernate就已經(jīng)是配置好的并且可用的。同時(shí)我們想當(dāng)application關(guān)閉的時(shí)候關(guān)閉Hibernate。我們可以用一個(gè)Hibernate PlugIn來(lái)實(shí)現(xiàn)這個(gè)需求,通過(guò)如下的兩步:

            1、創(chuàng)建一個(gè)類(lèi)實(shí)現(xiàn)了PlugIn接口

            public class HibernatePlugIn implements PlugIn{
            private String configFile;
            // This method will be called at application shutdown time
            public void destroy() {
            System.out.println("Entering HibernatePlugIn.destroy()");
            //Put hibernate cleanup code here
            System.out.println("Exiting HibernatePlugIn.destroy()");
            }
            //This method will be called at application startup time
            public void init(ActionServlet actionServlet, ModuleConfig config)
            throws ServletException {
             System.out.println("Entering HibernatePlugIn.init()");
             System.out.println("value of init parameter " +
             getConfigFile());
             System.out.println("Exiting HibernatePlugIn.init()");
            }
            public String getConfigFile() {
            return name;
            }
            public void setConfigFile(String string) {
            configFile = string;
            }
            }

            實(shí)現(xiàn)PlugIn接口的類(lèi)必須完成兩個(gè)方法:init()和destroy()。當(dāng)application startup的時(shí)候init()方法被調(diào)用,當(dāng)shutdown的時(shí)候destroy()方法被調(diào)用。Struts還允許給你的PlugIn類(lèi)傳遞初始化參數(shù)。為了傳遞參數(shù),你必須在PlugIn類(lèi)中為每一個(gè)參數(shù)創(chuàng)建JavaBean式的setter方法。在我們的HibernatePlugIn類(lèi)中,我會(huì)把configFile的name作為參數(shù)傳進(jìn)去,而不是硬編碼到程序中。

            2、在struts-config.xml中添加如下的代碼來(lái)通告Struts有新的PlugIn:

            <struts-config>
            ...
            <!-- Message Resources -->
            <message-resources parameter= "sample1.resources.ApplicationResources"/>

            <!-- Declare your plugins -->
            <plug-in className="com.sample.util.HibernatePlugIn">
            <set-property property="configFile" value="/hibernate.cfg.xml"/>
            </plug-in>
            </struts-config>

            屬性className是實(shí)現(xiàn)了PlugIn接口的類(lèi)的全限定名。對(duì)于每一個(gè)初始化參數(shù),可以使用<set-property>元素傳遞參數(shù)。在我們的例子中,我要把config文件的名字傳進(jìn)去,所以使用了一個(gè)帶有配置文件路徑的<set-property>。

            Struts的Tiles和Validator框架都使用PlugIn來(lái)讀取配置文件進(jìn)行初始化。另外兩件PlugIn可以幫你做到的事情是:

            ·如果你的application依賴(lài)于某些配置文件,那么你可以在PlugIn類(lèi)中檢查它們是否可用,如果不可用的話拋出一個(gè)ServletException,這樣就可以使ActionServlet變?yōu)椴豢捎谩?

            ·PlugIn接口的init()方法是你可以改變ModuleConfig的最后機(jī)會(huì),ModuleConfig是一組靜態(tài)配置信息的集合,用來(lái)描述基于Struts模塊。Struts將會(huì)在所有PlugIn處理完后釋放ModuleConfig。
             Request是如何被處理的

            ActionServlet是Struts框架中唯一的Servlet,它負(fù)責(zé)處理所有request。無(wú)論何時(shí)接收到一個(gè)request,它都會(huì)先嘗試為當(dāng)前的request尋找一個(gè)sub-application。一旦一個(gè)sub-application被找到,ActionServlet就會(huì)為那個(gè)sub-application創(chuàng)建一個(gè)RequestProcessor對(duì)象,調(diào)用這個(gè)對(duì)象的process()方法并把HttpServletRequest和HttpServletResponse對(duì)象傳入。

            RequestProcessor.process()就是大部分request被處理的地方。process()方法使用了Template Method模式實(shí)現(xiàn),其中有很多獨(dú)立的方法來(lái)執(zhí)行請(qǐng)求處理的每一步驟,這些方法將會(huì)在process方法中依次被調(diào)用。比如,將會(huì)有一個(gè)獨(dú)立的方法用來(lái)尋找當(dāng)前request對(duì)應(yīng)的ActionForm類(lèi),一個(gè)方法來(lái)檢查當(dāng)前用戶(hù)是否有執(zhí)行action mapping所必須的權(quán)限。這些給與我們極大的靈活性。在發(fā)布的Struts包中有一個(gè)RequestProcessor類(lèi)提供了請(qǐng)求處理每一步驟的默認(rèn)實(shí)現(xiàn)。這就意味著你可以?xún)H僅重寫(xiě)你感興趣的方法,其它的使用默認(rèn)的實(shí)現(xiàn)。舉例來(lái)說(shuō),默認(rèn)地Struts調(diào)用request.isUserInRole()來(lái)檢查用戶(hù)是否有權(quán)限執(zhí)行當(dāng)前的ActionMapping;這時(shí)如果你想通過(guò)查詢(xún)數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn),你所要做的就是重寫(xiě)processRoles()方法,通過(guò)查詢(xún)出的用戶(hù)是否擁有必須的權(quán)限來(lái)返回true或false。

            首先我們將會(huì)看到缺省情況下,process()方法是如何實(shí)現(xiàn)的,然后我將會(huì)詳細(xì)解釋默認(rèn)的RequestProcessor類(lèi)中的每一個(gè)方法,這樣你就可以決定哪一部分是你想要改變的。

            public void process(HttpServletRequest request,HttpServletResponse response)
            throws IOException, ServletException {
            // Wrap multipart requests with a special wrapper
            request = processMultipart(request);
            // Identify the path component we will
            // use to select a mapping
            String path = processPath(request, response);
            if (path == null) {
            return;
            }
            if (log.isDebugEnabled()) {
            log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");
            }
            // Select a Locale for the current user if requested
            processLocale(request, response);
            // Set the content type and no-caching headers
            // if requested
            processContent(request, response);
            processNoCache(request, response);
            // General purpose preprocessing hook
            if (!processPreprocess(request, response)) {
            return;
            }
            // Identify the mapping for this request
            ActionMapping mapping =
            processMapping(request, response, path);
            if (mapping == null) {
            return;
            }
            // Check for any role required to perform this action
            if (!processRoles(request, response, mapping)) {
            return;
            }
            // Process any ActionForm bean related to this request
            ActionForm form = processActionForm(request, response, mapping);
            processPopulate(request, response, form, mapping);
            if (!processValidate(request, response, form, mapping)) {
            return;
            }
            // Process a forward or include specified by this mapping
            if (!processForward(request, response, mapping)) {
            return;
            }
            if (!processInclude(request, response, mapping)) {
            return;
            }
            // Create or acquire the Action instance to
            // process this request
            Action action =
            processActionCreate(request, response, mapping);
            if (action == null) {
            return;
            }
            // Call the Action instance itself
            ActionForward forward = processActionPerform(request, response,action, form, mapping);
            // Process the returned ActionForward instance
            processForwardConfig(request, response, forward);
            }

            1、processMutipart():在這個(gè)方法中,Struts將會(huì)讀取request來(lái)檢查request的contentType是否是multipart/form-data。如果是的話,將會(huì)解析request并且將之包裝到HttpServletRequest中。當(dāng)你創(chuàng)建了一個(gè)HTML FORM用來(lái)提交數(shù)據(jù),那么request的contentType默認(rèn)就是application/x-www-form-urlencoded。但是如果你的form使用了file類(lèi)型的input控件允許用戶(hù)上傳文件的話,你就必須將contentType改為multipart/form-data。如果是這樣的情況,你就不能再通過(guò)getParameter()來(lái)獲取用戶(hù)提交的數(shù)據(jù);你必須將request作為一個(gè)InputStream來(lái)讀取,并且自己解析它來(lái)獲得參數(shù)值。

            2、processPath():在這個(gè)方法中,Struts將會(huì)讀取request的URI,來(lái)確定路徑元素,這個(gè)元素是用來(lái)獲取ActionMappint元素。
            3、processLocale():在這個(gè)方法中,Struts將會(huì)為當(dāng)前request取得Locale,如果配置過(guò)的話,還可以將這個(gè)對(duì)象作為HttpSession中org.apache.struts.action.LOCALE屬性的值而保存。作為這個(gè)方法的副作用,HttpSession將會(huì)被創(chuàng)建,如果你不想創(chuàng)建的話,你可以在ControllerConfig中將locale屬性設(shè)為false,在struts-config.xml中象如下這樣:

            <controller>
            <set-property property="locale" value="false"/>
            </controller>

            4、processContent():通過(guò)調(diào)用response.setContentType()來(lái)為response設(shè)置contentType。這個(gè)方法首先會(huì)嘗試從struts-config.xml配置中得到contentType。缺省情況下使用text/html。如果想覆蓋它,可以象如下這樣:

            <controller>
            <set-property property="contentType" value="text/plain"/>
            </controller>

            5、processNoCache():如果配置是no-cache,Struts將會(huì)為每個(gè)response設(shè)置下面三個(gè)headers:

            requested in struts config.xml
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 1);

            如果你想設(shè)置no-cache header,在struts-config.xml中加入下面信息:

            <controller>
            <set-property property="noCache" value="true"/>
            </controller>

            6、processPreprocess():這個(gè)方法為預(yù)處理提供一個(gè)hook,可以在子類(lèi)中覆蓋它。它的缺省實(shí)現(xiàn)沒(méi)有作任何事情,總是返回true。返回false的話將會(huì)終止當(dāng)前請(qǐng)求的處理。

            7、processMapping():這個(gè)方法將會(huì)用路徑信息得到一個(gè)ActionMapping對(duì)象。也就是struts-config.xml文件中的<action>元素:

            <action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request">
            <forward name="sucess" path="/sucessPage.do"/>
            <forward name="failure" path="/failurePage.do"/>
            </action>

            ActionMapping元素包含了Action類(lèi)的名稱(chēng)和處理請(qǐng)求使用的ActionForm等等信息。它還包含當(dāng)前ActionMapping配置的ActionForwards信息。

            8、processRoles():Struts web應(yīng)用提供了一個(gè)授權(quán)方案。也就是說(shuō),一旦一個(gè)用戶(hù)登入了容器,struts的processRoles()方法將會(huì)通過(guò)調(diào)用request.isUserInRole(),來(lái)檢查他是否有必須的角色來(lái)運(yùn)行一個(gè)給定的ActionMapping。

            <action path="/addUser" roles="administrator"/>

            假設(shè)你有一個(gè)AddUserAction并且你只想讓administrator能夠增加新的user。你所要做的就是給你的AddUserAction元素增加一個(gè)role屬性,這個(gè)屬性的值為administrator。這樣,在運(yùn)行AddUserAction之前,這個(gè)方法會(huì)確保用戶(hù)擁有administraotr的角色。

            9、processActionForm():每一個(gè)ActionMapping都一個(gè)相應(yīng)的ActionForm類(lèi)。當(dāng)Struts處理一個(gè)ActionMapping的時(shí)候,它將會(huì)從<action>元素的name屬性中找出對(duì)應(yīng)的ActionForm類(lèi)的名稱(chēng)。

            <form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm">
            <form-property name="firstName" type="java.lang.String"/>
            <form-property name="lastName" type="java.lang.String"/>
            </form-bean>

            在我們的例子中,它會(huì)先在request scope中檢查是否有一個(gè)org.apache.struts.action.DynaActionForm類(lèi)的對(duì)象存在。如果有它將會(huì)使用這個(gè)對(duì)象,如果沒(méi)有它將會(huì)創(chuàng)建一個(gè)新的對(duì)象并把它設(shè)置在request scope。

            10、processPopulate():在這個(gè)方法中,Struts將會(huì)用相匹配的request參數(shù)裝配ActionForm的實(shí)例變量。

            11、processValidate():Struts將會(huì)調(diào)用你的ActionForm類(lèi)的validate方法。如果你從validate()返回ActionErrors,它將會(huì)將user重定向到<action>元素的input屬性指定的頁(yè)面。

            12、processForward()和processInclude():在這些方法中,Struts將會(huì)檢查<action>元素的forward或include屬性,如果找到了,將會(huì)把forward或include請(qǐng)求放置到配置的頁(yè)面中。

            <action forward="/Login.jsp" path="/loginInput"/>
            <action include="/Login.jsp" path="/loginInput"/>

            你可以從這些方法的名字上猜測(cè)它們的不同:processForward()最終調(diào)用RequestDispatcher.forward(),而processInclude()調(diào)用RequestDispatcher.include()。如果你同時(shí)配置了forward和include屬性,它將會(huì)總是調(diào)用forward,因?yàn)閒orward先被處理。

            13、processActionCreate():這個(gè)方法從<action>元素的type屬性中獲取獲得Action類(lèi)的名字并且創(chuàng)建返回它的實(shí)例。在我們的例子中,它將會(huì)創(chuàng)建一個(gè)com.sample.NewContactAction類(lèi)的實(shí)例。

            14、processActionPerform():這個(gè)方法調(diào)用你的Action類(lèi)的excute()方法,你的業(yè)務(wù)邏輯也就是在excute方法中。

            15、processForwardConfig():你的Action類(lèi)的excute()方法將會(huì)返回一個(gè)ActionForward對(duì)象,這個(gè)對(duì)象將指出哪個(gè)頁(yè)面是顯示給用戶(hù)的頁(yè)面。因此,Struts將會(huì)為那個(gè)頁(yè)面創(chuàng)建一個(gè)RequestDispatcher,并且調(diào)用RequestDispatcher.forward()。

            上面的列表說(shuō)明了默認(rèn)的RequestProcessor實(shí)現(xiàn)在處理請(qǐng)求時(shí)每一步作的工作,以及執(zhí)行的順序。正如你所看到的,RequestProcessor是非常靈活的,允許你通過(guò)設(shè)置<controller>元素的屬性來(lái)配置它。舉例來(lái)說(shuō),如果你的應(yīng)用準(zhǔn)備生成XML內(nèi)容來(lái)代替HTML,你就可以通過(guò)設(shè)置controller元素的屬性來(lái)通知Struts這些情況。
            
            創(chuàng)建你自己的RequestProcessor

            通過(guò)上面,我們了解到了RequestProcessor的默認(rèn)實(shí)現(xiàn)是如何工作的。現(xiàn)在我們要演示一個(gè)例子來(lái)說(shuō)明如何定制你自己的RequestProcessor。為了展示創(chuàng)建用戶(hù)定制的RequestProcessor,我們將會(huì)讓我們的示例實(shí)現(xiàn)下面兩個(gè)業(yè)務(wù)需求:

            ·我們想創(chuàng)建一個(gè)ContactImageAction類(lèi),它將生成圖片而不是平常的HTML頁(yè)面。

            ·在每個(gè)請(qǐng)求處理之前,我們都想通過(guò)檢查session中的userName屬性來(lái)確定用戶(hù)是否已經(jīng)登陸。如果那個(gè)屬性沒(méi)有找到,我們會(huì)把用戶(hù)重定向到登陸頁(yè)面。

            我們將分兩步實(shí)現(xiàn)這些業(yè)務(wù)需求。

            1、創(chuàng)建你的CustomRequestProcessor類(lèi),它將繼承自RequestProcessor類(lèi),如下:

            public class CustomRequestProcessor
            extends RequestProcessor {
            protected boolean processPreprocess (
            HttpServletRequest request,HttpServletResponse response) {
             HttpSession session = request.getSession(false);
             //If user is trying to access login page
             // then don't check
             if( request.getServletPath().equals("/loginInput.do")
            || request.getServletPath().equals("/login.do") )
              return true;
             //Check if userName attribute is there is session.
             //If so, it means user has allready logged in
             if( session != null && session.getAttribute("userName") != null)
              return true;
             else{
              try{
               //If no redirect user to login Page
               request.getRequestDispatcher("/Login.jsp").forward(request,response);
              }catch(Exception ex){
              }
             }
             return false;
            }

            protected void processContent(HttpServletRequest request,
            HttpServletResponse response) {
            //Check if user is requesting ContactImageAction
            // if yes then set image/gif as content type
            if( request.getServletPath().equals("/contactimage.do")){
             response.setContentType("image/gif");
             return;
            }
             super.processContent(request, response);
            } 
            }

            在CustomRequestProcessor類(lèi)的processPreprocess方法中,我們檢查session的userName屬性,如果沒(méi)有找到,就將用戶(hù)重定向到登陸頁(yè)面。

            對(duì)于生成圖片作為輸出的需求,我們必須覆蓋processContent方法,首先檢查請(qǐng)求是否是/contactimage路徑。如果是的話,我們就會(huì)將contentType設(shè)置為image/gif;否則設(shè)置為text/html。

            2、在你的struts-config.xml文件的<action-mappint>元素之后加入下面的文字,告訴Struts CustomRequestProcessor應(yīng)當(dāng)被用作RequestProcessor類(lèi):

            <controller>
            <set-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/>
            </controller>

            請(qǐng)注意,當(dāng)你只有很少的action類(lèi)需要生成非text/html類(lèi)型的輸出時(shí),你覆寫(xiě)processContent()方法是OK的。如果不是這樣子的話,你應(yīng)該創(chuàng)建一個(gè)Struts的子應(yīng)用來(lái)處理請(qǐng)求生成圖片的Action,并為它們將contentType設(shè)置為image/gif。

            Struts的Tiles框架就是使用它自己的RequestProcessor來(lái)裝飾Struts的輸出。

            ActionServlet

            如果你查看你的Struts web應(yīng)用的web.xml,你會(huì)看到這樣的文字:

            <web-app >
            <servlet>
            <servlet-name>action=</servlet-name>
            <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
            <!-- All your init-params go here-->
            </servlet>
            <servlet-mapping>
            <servlet-name>action</servlet-name>
            <url-pattern>*.do</url-pattern>
            </servlet-mapping>
            </web-app >

            這意味著ActionServlet負(fù)責(zé)處理你所有Struts的請(qǐng)求。你可以創(chuàng)建一個(gè)ActionServlet的子類(lèi),當(dāng)應(yīng)用啟動(dòng),關(guān)閉,每個(gè)請(qǐng)求的時(shí)候做一些特定的事情。但是在繼承ActionServlet類(lèi)之前,你應(yīng)該盡量創(chuàng)建一個(gè)PlugIn或RequestProcessor去解決你的問(wèn)題。在Servlet1.1之前,Tiles框架是基于ActionServlet來(lái)修飾生成的響應(yīng)。但是從1.1之后,它開(kāi)始使用TilesRequestProcessor類(lèi)。

            總結(jié)

            決定開(kāi)發(fā)你自己的MVC框架是一個(gè)非常大的決定,你必須要考慮開(kāi)發(fā)和維護(hù)框架代碼所花費(fèi)的時(shí)間和資源。Struts是一個(gè)非常強(qiáng)大和穩(wěn)定的框架,你可以修改它來(lái)滿(mǎn)足你絕大多數(shù)的業(yè)務(wù)需求。

            但另一方面,也不要草率地做出擴(kuò)展Struts的決定。如果你在RequestProcessor中寫(xiě)了一些性能比較低的代碼,它將會(huì)在每次請(qǐng)求時(shí)執(zhí)行,因而降低你整個(gè)應(yīng)用的效率。而且還是有一些情況,開(kāi)發(fā)自己的MVC框架要比擴(kuò)展Struts好。
          posted on 2007-08-31 12:42 楊?lèi)?ài)友 閱讀(1691) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): java相關(guān)技術(shù)
          美麗涵涵童裝店
          親,說(shuō)我博客名字,給你們打折!
          主站蜘蛛池模板: 大关县| 天津市| 神池县| 山西省| 共和县| 湖北省| 左权县| 荆门市| 聂荣县| 涟源市| 肇源县| 澄江县| 邢台市| 清苑县| 石狮市| 丰城市| 邵阳县| 宁乡县| 射洪县| 焦作市| 营口市| 红桥区| 云龙县| 全南县| 北辰区| 中山市| 当涂县| 阿瓦提县| 揭东县| 丹阳市| 环江| 滕州市| 固安县| 黔西县| 怀安县| 民权县| 武鸣县| 安多县| 志丹县| 雷州市| 喀什市|