<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Cyh的博客

    Email:kissyan4916@163.com
    posts - 26, comments - 19, trackbacks - 0, articles - 220

    筆記之Spring-MVC

    Posted on 2009-02-21 00:34 啥都寫點 閱讀(1942) 評論(0)  編輯  收藏 所屬分類: J2EE

     

    • 請求生命中的第一天:請求從離開瀏覽器開始知道獲得一個響應(yīng),期間會有幾次停留,每一次都留下一些信息并得到更多的信息。(見PPT1)

      • 配置DispatcherServlet:Spring MVC的核心是DispatcherServlet,這個servlet的功能是作為Spring MVC的前端控制器。和任何Servlet一樣,必須在Web應(yīng)用系統(tǒng)的web.xml文件中配置。

        <servlet>

            <servlet-name>roadrantz</servlet-name>

            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

            <load-on-startup>1</load-on-startup>

        </servlet>

        當(dāng)DispatcherServlet載入后,它將從XML中載入Spring的應(yīng)用上下文,這個XML的名字取決于Servlet的名字。在本例中,因為Servlet的名字叫做roadrantz,所以DispatcherServlet將試圖從一個叫做roadrantz-servlet.xml的文件中載入應(yīng)用上下文。接下來指定哪些URL需要由DispatcherServlet來處理。

        <servlet-mapping>

              <servlet-name>roadrantz</servlet-name>

              <url-pattern>*.htm</url-pattern>

        </servlet-mapping>  我們可以為DispatcherServlet選擇任意URL樣式。我們選擇"*.htm"樣式的主要原因是因為這種樣式是大多數(shù)生成HTML內(nèi)容的Spring MVC應(yīng)用系統(tǒng)的慣例用法。另一個原因是生成的內(nèi)容是HTML,因此URL應(yīng)該反映這一點。

        • 分解應(yīng)用上下文:正如前面提到的,DispatcherServlet從以<servlet-name>命名的XML文件中載入應(yīng)用上下文。但這不是說你不能將你的應(yīng)用上下文切到多個XML文件中。事實上,我們建議你將應(yīng)用上下文分散到應(yīng)用系統(tǒng)的各個層中。(見PPT2)由于DispatcherServlet的配置文件是roadrantz-servlet.xml,所以在這個文件中應(yīng)該包含用于控制器和其他Spring MVC組件的<bean>定義信息。對于業(yè)務(wù)層和數(shù)據(jù)層的Bean,我們傾向于將他們分別放到roadrantz-service.xml和roadrantz-data.xml中。

          配置上下文載入器:為了保證所有的配置文件都被載入,需要在web.xml中配置一個上下文載入器。上下文載入器載入除DispatcherServlet載入的配置文件之外的其他上下文配置文件。最常用的上下文載入器是一個Servlet監(jiān)聽器,其名稱為ContextLoaderListener,你需要在web.xml文件中像下面這樣配置它:

          <listener>

              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

          </listener>    使用ContextLoaderListener配置時,你需要告訴它Spring配置文件的位置。如果沒有指定,上下文載入器會在/WEB-INF/applicationContext.xml中找Spring配置文件。但是這樣無法將應(yīng)用上下文分散到應(yīng)用系統(tǒng)的各個層中,所以你需要取代這種默認(rèn)方式。 你可以通過Servlet上下文中設(shè)置contextConfigLocation參數(shù)來為上下文載入器指定一個或多個Spring配置文件:

          <context-param>

               <param-name>contextConfigLocation</param-name>

               <param-value>

                    /WEB-INF/roadrantz-service.xml,

                    /WEB-INF/roadrantz-data.xml,

                    /WEB-INF/roadrantz-security.xml

               </param-value>

          </context-param>

          Spring MVC概述:每一個Web應(yīng)用程序都有一個主頁。這是應(yīng)用程序的一個起始點。用戶可以從主頁中啟動應(yīng)用程序,也可以在失去方向時回到主頁。否則,用戶會不停的點擊鏈接,感到很困擾,甚至可能離開,進入到其他網(wǎng)站中。第一步是建立一個處理主頁請求的控制器對象。因此讓我們來編寫第一個Spring MVC控制器。

          • 創(chuàng)建控制器:在Spring MVC中,控制器是一個與應(yīng)用程序功能的接口類。(見PPT3)控制器接收請求,將請求發(fā)送給服務(wù)類進行處理,最后又收集需要返回給用戶Web瀏覽器的結(jié)果。

            public class HomePageController extends AbstractController {

                protect ModelAndView handleRequestInternal(HttpServletRequest request,HttpServletResponse response)throws Exception {

                 List recentRants = rantService.getRecentRants( );

                return new ModelAndView("home","rants",recentRants);

                }

               private RantService rantService;

               public void setRantService(RantService rantService) {

                  this.rantService = rantService;

                }

              }

            } Spring控制器和Servlet或Struts Action的不同之處在于它被配置成Spring應(yīng)用上下文的一個普通JavaBean。這意味著,使用控制器和使用其他Bean一樣,可以充分利用依賴注入和Spring AOP。在HomePageController中,依賴注入用在了注入一個RantService.HomePageController

            引入ModelAndView:控制器執(zhí)行方法都必須返回一個ModelAndView。因此需要理解這個重要的類是如何工作的。正如它的名字表述的,ModelAndView保存了視圖以及視圖顯示的模型數(shù)據(jù).在HomePageController里,ModelAndView對象應(yīng)按照下面的方式構(gòu)建: new ModeAndView("home","rants",recentRants); 構(gòu)造器的第一個參數(shù)是視圖組件(用于顯示空氣器的輸出)的邏輯名稱。這里,視圖的邏輯名稱是home。視圖解析器會使用這個名稱查找實際的View對象  。 后兩個參數(shù)分別表示傳遞給視圖的模型對象。這個兩個參數(shù)是一個名字值對。第二個參數(shù)就是第三個參數(shù)所表示的模型對象名稱。

            配置控制器Bean:現(xiàn)在HomePageController已經(jīng)寫好了,你必須將其配置到DispatcherServlet的上下文配置文件中(對于RaodRantz應(yīng)用程序就是roadrantz-servlet.xml文件)。下面這段XML代碼定義了HomePageController:

            <bean name="/home.htm" class="com.roadrantz.mvc.HomePageController">

               <property name="rantService" ref="rantService" />

            </bean>  有件事讓你感到奇怪,不是為HomePageController Bean設(shè)置id屬性,而是設(shè)置name屬性。并且更奇怪的是設(shè)定的不是一個真實名字,而是給它設(shè)置一個URL"/home.htm"。這里name屬性承擔(dān)了兩個責(zé)任,即定義Bean的名字也定義需要使用這個控制器處理的URL樣式。由于URL樣式含有XMLid屬性中的非法字符--特別是斜杠(/),所以使用name屬性,而不使用id。只要進入DispatcherServlet的請求是以"/home.htm"結(jié)尾的,DispatcherServlet就會分給HomePageController來處理,注意這個Bean的name屬性使用的是URL樣式的唯一原因是我們還沒有配置處理映射Bean。DispatcherServlet使用的默認(rèn)處理器映射是BeanNameUrlHandlerMapping,它使用URL樣式的名字。

            聲明一個視圖解析器:返回給ModelAndView對象的其中一個值是邏輯視圖名稱。然而這個邏輯視圖名稱并不是直接引用特定的JSP的,而是用于間接的表達使用哪一個JSP。為了幫助Spring MVC了解使用哪一個JSP,你需要在roadrantz-servlet.xml中聲明另一個Bean:一個視圖解析器。簡單來看,視圖解析器的工作就是將視圖的名稱返回到ModelAndView中并將其映射到一個視圖上。在HomePageController中,我們需要一個視圖解析器將"home"解析成一個JSP文件來呈現(xiàn)主頁。Spring MVC帶來了很多可供選擇的視圖解析器。對于使用JSP渲染視圖來說,再也沒有比InteralResourceViewResolver更簡單的了:

              <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">

                   <property name="prefix">

                      <value>/WEB-INF/jsp/</value>

                   </property>

                   <property name="suffix">

                      <value>.jsp</value>

                   </property>

               </bean>由于HomePageController返回的ModelAndView中的視圖名是home,InternalResourceViewResolver將在/WEB-INF/jsp/home.jsp處查找視圖。

      將請求映射到控制器:當(dāng)請求到達DispatcherServlet時,需要一些目錄來指明請求應(yīng)該如何分配。處理器映射可以幫助DispatcherServlet了解請求應(yīng)該被發(fā)送給哪個控制器。我們依賴DispatcherServlet默認(rèn)使用的BeanNameUrlHandMapping。BeanNameUrlHandMapping很容易上手,但是 它無法滿足所有情況。所幸,Spring MVC提供了幾種可以選擇的處理器映射實現(xiàn)。Spring MVC中所有的處理器映射都實現(xiàn)了接口org.springframework.web.servlet.HandlerMapping  (見PPT4)

      • 使用SimpleUrlHandlerMapping:它可能是最直接的Spring處理器映射。它允許你將URL樣式直接映射到控制器,而且不需要以待定的方式命名你的Bean。例如:

           <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

             <property name="mappings">

                <props>

                   <prop key="/home.htm">homePageController</prop>

                   <prop key="/rantsForVehicle.htm">rantsForVehicleControllerRss</prop>

                   <prop key="/rantsForVehicle.rss">rantsForVehicleControllerRss</prop>

                </props>

             </property>

           </bean>

        <props>元素的key屬性是URL樣式。和BeanNameUrlHandlerMapping一樣,所有的URL樣式和DispatcherServlet的<servlet-mapping>一一對應(yīng)。<prop>的值是處理這個URL的控制器Bean的名字。

        使用ControllerClassNameHandlerMapping:很多時候,你會發(fā)現(xiàn)控制器映射的URL樣式都與控制器類名很相似。例如,在RoadRantz應(yīng)用程序中,我們將rantForVehicle.htm映射到rantForVehicleController,將rantsForDay.htm映射到RantsForDayController。在這些例子中,URL樣式與控制器的類名相同,只不過去掉了Controller部分,加上了.htm部分。這種樣式就好像對映射設(shè)置了特定的默認(rèn)值,而不需要明確地進行映射。這大概就是ControllerClassNameHandlerMapping所做的工作:

        <bean id="urlMapping" class="org.springframework.web.servlet.mvc.ControllerClassNameHandlerMapping" />

        這樣就可以通知Spring的DispatcherServlet將URL樣式按照簡單的約定映射到控制器。Spring不必明確的為每個控制器映射URL樣式,而是可以根據(jù)控制器的類名自動的將控制器映射到URL樣式上。 為了簡單起見,要想生成URL樣式,控制器類名中的Controller部分會被去掉(如果這部分存在),剩下的文本會變成小寫字母,并且在最前面加上一個反斜杠/,在最后加上".htm"。

        使用CommonsPathMapHandlerMapping元數(shù)據(jù)映射控制器:它是根據(jù)控制器源代碼中的元數(shù)據(jù)決定如何進行URL映射。這個元數(shù)據(jù)特定是一個org.springframework.web.servlet.handler.commonsattributes.PathMap屬性,是用Jakarta Commons Attributes編譯器編譯到控制器中。 要使用它,只要像下面這樣簡單地在上下文控制文件中定義一個Bean就可以了:

        <bean id="urlMapping" class="org.springframework.web.servlet.handler.metadata.CommonsPathMapHandlerMapping" />

        控制器源代碼注釋標(biāo)簽中的PathMap屬性聲明了這個控制處理的URL樣式。例如,為了將HomePageController映射到"/home.htm",HomePageController的標(biāo)簽是這樣的:

        /**

         * @@org.springframework.web.servlet.handler.commonsattributes.PathMap("/home.htm")

        */

        public class HomePageController extends AbstractController {

         ......

        }  最后,編譯的時候需要將Commons Attributes編譯器放進來,這樣標(biāo)簽PathMap 才能被編譯到應(yīng)用代碼中。要詳細(xì)了解如何在Ant 或Maven中設(shè)置Commons Attributes編譯器,請參考Commons Attributes 主頁(http://jakarta.apache.org/commons/attributes)。

        使用多映射處理器:所有的處理器映射類都實現(xiàn)了Spring的Ordered接口。這意味著你可以在應(yīng)用系統(tǒng)中聲明多個處理器映射,并且設(shè)置哪個相對另一個有優(yōu)先權(quán)。例如,假設(shè)想在一個應(yīng)用系統(tǒng)中并排使用BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping。你需要像下面這樣聲明處理器映射Bean:

        <bean id="beanNameUrlMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">

          <property name="order">

            <value>1</value>

          </property>

        </bean>

        <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

          <property name="order"><value>0</value> </property> 

          <property name="mappings">

           ...

          </property>

        </bean>

        注意,order屬性的值越小,優(yōu)先級越高。

      用控制器處理請求:如果DispatcherServlet是Spring MVC的心臟,那么控制器就是它的大腦。Spring控制器與Struts/web work action相比有一個重要的區(qū)別。這就是,相比Struts或Web Work比較平坦的Action層次,Spring提供了豐富的控制器層(見PPT5)在控制器層次的最上面是接口Controller,任何實現(xiàn)這個接口的類都可以用來處理Spring MVC框架傳遞過來的請求。要創(chuàng)建自己的控制器,你必須要做的就是實現(xiàn)這個接口。Spring讓你選擇最適合自己需要的控制器(見PPT6)Spring控制器可以歸為6種類型,沿著表格向下,功能越來越多,除了ThrowawayController,順著控制器類層次向下,每個控制器都是建立在它上面的控制器基礎(chǔ)之上的。

      • 處理命令:當(dāng)控制器需要根據(jù)參數(shù)執(zhí)行工作時,應(yīng)該繼承命令控制器,如AbstractCommandController(見PPT7)這個控制器會自動將參數(shù)綁定到命令對象中,并且提供了插入驗證器的鉤子,確保參數(shù)合法性。下面顯示了RantsForVehicleController,一個用于顯示特定車輛已有投訴列表的命令控制器。

        public class RantsForVehicleController  extends AbstractCommandController{

        public RantsForVehicleController() {

        setcommandClass(Vehicle.class);

        setCommandName("vehicle");

        }

        protected ModelAndView handle(HttpServletRequest request,HttpServletResponse,Object command,

        BindException errors) throws Exception {

               Vehicle vehicle = (Vehicle) command;

               List vehicleRants = rantService.getRantsForVehicle(vehicle);

               Map model =errors.getModel();

               model.put("rants", rantService.getRantsForVehicle(vehicle));

               model.put("vehicle", vehicle);

                return new ModelAndView("vehicleRants",model);

         }

        private RantService rantService;

        public void setRantService(RantService rantService) {

        this.rantService = rantService;

         }

        }

        一個命令對象是一個為了簡化對請求參數(shù)訪問而設(shè)計的Bean。一個Spring命令對象是一個POJO,它不需要實現(xiàn)任何Spring的特定類。你需要在roadrantz-servlet.xml文件中注冊RantsForVehicleController

        <bean id="rantsForVehicleController" class="com.roadrantz.mvc.RantsForVehicleController">

           <property name="rantService" ref="rantService"/>

        </bean>

        處理表單提交:表單控制器比命令控制器前進了一步,(見PPT8)它在接收到HTTP GET請求的時候顯示一個表單,接收到一個HTTP POST請求的時候處理這個表單。另外,在處理過程中如果發(fā)生錯誤的話,這個控制器會知道重新顯示這個表單,這樣用戶就可以修改錯誤,重新提交。為了顯示表單控制器是如何工作的,考慮以下程序中的AddRantFormController。

        public class AddRantFormController extends SimpleFormController {

        private static final String[] ALL_STATES = {"AL","AK","AZ"};

           public AddRantFormController() {

           setCommandClass(Rant.class);

           setCommandName("rant");

           }

           protected Object formBackingObject(HttpServletRequest request) throws Exception {

           Rant rantForm = (Rant)super.formBackingObject(request);

           rantForm.setVehicle(new Vehicle());

           }

           protected Map referenceDate(HttpServletRequest request) throws Exception{

           Map referenceData = new HashMap();

           referenceDate.put("states",ALL_STATES);

           return referenceData;

           }

           protected ModelAndView onSumbit(Object command,BindException bindException)throws Exception{

           Rant rant = (Rant)command;

           rantService.addRant(rant);

           return new ModelAndView(getSuccessView());

           }

           private RantService rantService;

        public void setRantService(RantService rantService) {

        this.rantService = rantService;

        }

        }雖然referenceData()方法是可選的,但是如果需要為 顯示表單提供其他附加的信息,則可以使用這個方法。在正常的情況下,返回表單的命令對象一般是一個簡單的命令類實例。不過對于AddRantFormController,Rant實例不會完成這項工作。表單會使用內(nèi)嵌的Vehicle屬性作為表單返回對象的一部分。因此,需要覆蓋fromBackingObject()方法,以便設(shè)置vehicle屬性。否則,在控制器視圖綁定state和plateNumber屬性時,會拋出NullPointerException異常。

             那么控制器是如何知道顯示投訴輸入表單的。如果投訴輸入成功,用戶將到什么頁面也不是很清楚。唯一的線索是對getSuccessView()的調(diào)用結(jié)果會提交給ModelAndView。但是,提交成功的視圖又從哪里來呢?SimpleFormController被設(shè)計成盡量將視圖詳細(xì)信息放在控制器代碼之外。不是將ModelAndView對象硬編碼進來,而是像下面這樣在上下文配置文件中配置控制器:

        <bean id="addRantController" class="com.roadrantz.mvc.AddRantFormController">

             <property name="formView"  value="addRant" />

             <property name="successView" value="rantAdded" />

             <property name="rantService" ref="rantService" />

        </bean> formView屬性是控制器接收到HTTP GET請求或有任何錯誤發(fā)生時需要顯示的視圖的邏輯名。同樣,successView是提交的表單成功處理后要顯示的視圖的邏輯名。

        驗證表單輸入:org.springframework.validation.Validator接口為Spring MVC提供了驗證功能,定義如下:

        public interface Validator {

            void validate(Object obj  , Errors errors);

            boolean supports(Class clazz);

        }這個接口的實現(xiàn)必須驗證傳遞給validate()方法的對象的字段,用Errors對象駁回任何非法數(shù)據(jù)。supports()方法用于幫助Spring判斷該驗證器是否可以用于指定類。 以下是一個用于驗證Rant對象的Validator實現(xiàn)。

        public class RantValidator implements Validator {

          public boolean supports(Class clazz){

          return clazz.equals(Rant.class);

          }

          public void validate(Object command, Errors errors) {

          Rant rant = (Rant) command;

          ValidationUtils.rejectIfEmpty(errors,"vehicle.state","required.state","State is required");

          ValidationUtils.rejectIfEmptyOrWhitespace(errors, "rantText", "required.rantText","You must enter some rant text.");

              validatePlateNumber(rant.getVehicle().getPlateNumber(),errors);

          }

          private static final String PLATE_REGEXP = "/[a-z0-9]{2,6}/i";

          private void validatePlateNumber(String plateNumber,Errors errors) {

          Per15Util per15Util = new Per15Util();

          if(!per15Util.match(PLATE_REGEXP,plateNumber)){

          errors.reject("invalid.plateNumber","Invalid license plate number.");

          }

          }

        }還有一件事情要做,就是讓AddRantFormController使用RantValidator。你可以將一個RantValidator Bean裝配到AddRantFormController Bean中:

        <bean id="addRantController" class="com.roadrantz.mvc.AddRantFormController">

          <property name="formView" value="addRant"/>

          <property name="successView" value="rantAdded"/>

          <property name="rantService"  ref="rantService"/>

          <property name="validator">

             <bean class="com.roadrantz.mvc.RantValidator"></bean>

          </property>

        </bean>通過實現(xiàn)Validation接口,可以通過程序完全控制應(yīng)用程序命令對象的驗證。如果需要復(fù)雜的驗證和特殊的邏輯,利用這項功能將會十分方便。對于簡單的情況,例如保證填入需要的字段并按照基本格式,編寫自己的Validator接口就有點麻煩了。聲明性驗證是一個不錯的選擇。

        利用命令驗證器進行驗證:在我們深入研究用于實現(xiàn)聲明性Validator的Spring JavaDoc之前,需要知道Spring并不提供這樣的驗證器。Spring沒有提供任何Validator接口的實現(xiàn),而是將這個任務(wù)留給了程序員。 不過Spring Modules項目(http://springmodules.dev.java.net)是Spring的一個姊妹項目,提供了幾個對Spring的擴展。其中一個是使用Jakarta Commons Validator(http://jakarta.apache.org/commons/validator)提供的聲明性驗證的驗證模塊。

             要使用驗證模塊,首先需要添加springmodules-validator,jar  如果使用Ant建立應(yīng)用程序,需要下載Spring Modules發(fā)行包,找到spring-modules-0.6.jar文件,將這個JAR添加到<war>任務(wù)的<lib>中。如果使用Maven2建立應(yīng)用程序,需要在pom.xml中添加下面的<dependency>:

        <dependency>

           <groupId>org.springmodules</groupId>

           <artifactId>springmodules-validation</artifactId>

           <version>0.6</version>

           <scope>compile</scope>

        </dependency>

         另外,還需要將Jakarta Commons Validator JAR添加到應(yīng)用程序的classpath中,在Maven中,按如下所示:

        <dependency>

           <groupId>commons-validator</groupId>

           <artifactId>commons-validator</artifactId>

           <version>1.1.4</version>

           <scope>compile</scope>

        </dependency>

        Spring Module提供的Validator實現(xiàn)的名稱為DefaultBeanValidator。DefaultBeanValidator需要按下面的方式配置在roadrantz-servlet.xml中:

        <bean id="beanValidator" class="org.springmodules.commons.validator.DefaultBeanValidator">

            <property name="validatorFactory" ref="validatorFactory"></property>

        </bean>

        DefaultBeanValidator并不做任何實際的驗證工作,而是委派Commons Validator來驗證字段的值。validatorFactory Bean需要使用下面的XML進行聲明:

          <bean id="validatorFactory" class="org.springmodules.commons.validator.DefaultValidatorFactory">

            <property name="validationConfigLocations">

               <list>

                  <value>WEB-INF/validator-rules.xml</value>

                  <value>WEB-INF/validator.xml</value>

               </list>

            </property>

        </bean> validator-rules.xml文件包含了一組預(yù)定義的驗證規(guī)則,可以應(yīng)用于一般的驗證需求。這個文件被包含在Commons Validator發(fā)行包中,因此,你不需要自己編寫--值需要簡單的將其添加到應(yīng)用程序的WEB-INF目錄中。(PPT9)中列出了validator-rules.xml中的所有驗證規(guī)則。另一個文件validation.xml定義了應(yīng)用程序制定的驗證規(guī)則,可有直接應(yīng)用于RoadRantz應(yīng)用程序。下面展示了應(yīng)用到RoadRantz的validation.xml

        <?xml version="1.0" encoding="UTF-8"?>

        <!DOCTYPE form-validation PUBLIC"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1

           //EN" "http://jakarta.apache.org/commons/dtds/validator_1_1.dtd" >

        <form-validation>

            <formet>

                <form>

                   <field property="rantText" depends="required">

                      <arg0 key="required.rantText"/>

                   </field>

                   <field property="vehicle.plateNumber" depends="required,mask">

                     <arg0 key="invalid.plateNumber"/>

                     <var>

                        <var-name>mask</var-name>

                        <var-value>^[0-9A-Za-z]{2,6}$</var-value>

                     </var>

                   </field>

                </form>

            </formet>

        </form-validation>

        最后一件要做的事情是改變控制器的聲明,注入新的聲明性Validate實現(xiàn):

        <bean id="addRantController" class="com.roadrantz.mvc.AddRantFormController">

           <property name="formView" value="addRant" />

           <property name="successView" value="rantAdded" />

           <property name="rantService" value="rantService" />

           <property name="validator" value="beanValidator" />

        </bean>

          使用SimpleFormController的一個基本的原因是表單只有一頁。

        用向?qū)幚韽?fù)雜表單:AbstractWizardFormController是Spring提供的功能最強大的控制器(見PPT10)它是一種特殊類型的表單控制器,它將多個頁面中的表單數(shù)據(jù)聚集到一個用于處理的命令對象中。

        • 創(chuàng)建一個基本的向?qū)Э刂破?/strong>:構(gòu)建一個向?qū)Э刂破鳎仨毨^承AbstractWizardFormController類,如下:

          public class MotoristRegistrationController extends AbstractWizardFormController {

            public MotoristRegistrationController() {

             setCommandClass(Motorist.class);

             setCommandName("motorist");

            }

            protected Object formBackingObject(HttpServletRequest request) throws Exception{

          Motorist formMotorist = new Motorist();

          List<Vehicle> vehicles = new ArrayList<Vehicle>();

          vehicles.add(new Vehicle());

          formMotorist.setVehicles(vehicles);

          return formMotorist;

            }

            protected Map referenceData(HttpServletRequest request,Object command,Errors errors,int page)

             throws Exception{

          Motorist motorist = (motorist) command;

          Map refData = new HashMap();

              if(page==1 && request.getParameter("_target1")!= null){

               refData.put("nextVehicle", motorist.getVehicles().size()-1);

              }

              return refData;

             }

            protected void postProcessPage(HttpServletRequest request,Object command,Errors errors,int page)throws Exception {

          Motorist motorist = (Motorist)command;

          if(page==1 && request.getParameter("_target1")!= null){

              motorist.getVehicles().add(new Vehicle());

             }

            }

            protected ModelAndView  processFinish(HttpServletRequest request,HttpServletResponse response,

            Object command,BindException errors)throws Exception {

          Motorist motorist = (motorist) command;

          // the last Vehicle is always blank...remove it

          motorist.getVehicles().remove(motorist.getVehicles().size()-1);

          rantService.addMotorist(motorist);

          return new ModelAndView(getSuccessView(),"motorist",motorist);

            }

            //inject

            private RantService rantService;

            public void setRantService(RantService rantService) {

          this.rantService = rantService;

            }

            //returns the last page as the success view

            private String getSuccessView() {

            return getPages()[getPages().length-1];

            }

          }

          AbstractWizardFormController的唯一一個必須實現(xiàn)的方法是processFinish()。在用戶完成表單后(一般是點擊完成按鈕),這個方法被調(diào)用,完成整個表單。 但是AbstractWizardFormController是如何知道哪些頁面構(gòu)成了整個表單呢?

          <bean id="registerMotoristController" class="com.roadrantz.mvc.MotoristRegistrationController">

             <property name="rantService" ref="rantService" />

             <property name="pages">

                <list>

                  <value>motoristDetailForm</value>

                  <value>motoristVehicleForm</value>

                  <value>motoristConfirmation</value>

                  <value>redirect:home.htm</value>

                </list>

             </property>

          </bean>這樣向?qū)Ь椭懒四男╉撁鏄?gòu)成了表單,一個視圖邏輯名列表設(shè)置給了pages屬性。這些名字最終被視圖解析器解析成了一個個視圖對象。但現(xiàn)在,只要假設(shè)這些名字會被解析成JSP文件名就可以了。

          分步顯示表單頁面:任何向?qū)Э刂破黠@示的第一個頁面都是pages屬性中列表的第一個頁面。AbstractWizardFormController詢問它的getTargetPage()方法。這個方法返回一個整數(shù),它是pages屬性中設(shè)置的頁面列表的索引值(以0為基數(shù))。getTargetPage()方法的默認(rèn)實現(xiàn)是根據(jù)請求中的一個參數(shù)來決定下一步是哪個頁面,這個參數(shù)以"_target"開頭,以數(shù)字結(jié)尾。知道了getTargetPage()的工作原理有助于你知道如何在向?qū)TML頁面中構(gòu)造下一步和上一步按鈕。例如,假設(shè)用戶在"motoristVehicleForm"頁面上(索引=1)。要在這個頁面上創(chuàng)建下一步和上一步按鈕,你要做就是創(chuàng)建提交按鈕,它的名字是以"_target"開頭:

          <form>

          ...

            <input type="submit" value="Back" name="_target0">

            <input type="submit" value="Next" name="_target2">

          </form>getTargetPage()方法的默認(rèn)實現(xiàn)對于大多數(shù)項目已經(jīng)夠用了。然后,如果你喜歡為你的向?qū)Фx自己的工作流程的話,你可以覆寫這個方法。

          完成向?qū)?/strong>:還有一個特殊請求的參數(shù)"_finish",和"_targetX"參數(shù)一樣,它可以被用于在頁面上創(chuàng)建一個結(jié)束按鈕:

          <form method="POST" action="feedback.htm">

          ...

            <input type="submit" value="Finish" name="_finish">

          </form>

          當(dāng)AbstractWizardFormController看到請求中的"_finish"參數(shù)時,它會將控制權(quán)交給processFinish()方法,讓它對表單做最后的處理。與其他表單控制器不同,AbstractWizardFormController不提供用于設(shè)置成功視圖頁面的內(nèi)容。因此,我們在MotoristRegisttrationController中添加了一個getSuccessView()方法返回頁面列表中的最后一個頁面。所以,當(dāng)表單已完成的方式提交時,processFinish()方法會返回一個帶有視圖的ModelAndView,這個視圖就是頁面列表中的最后一個視圖。

          取消向?qū)?/strong>:如何用戶完成了部分注冊,決定不再繼續(xù)完成,除了選擇直接關(guān)閉瀏覽器外,還有另一種選擇,你可以在表單上添加一個取消按鈕。

          <form method="POST" action="feedback.htm">

          ...

             <input type="submit" value="Cancel" name="_cancel">

          </form>

          取消按鈕以"_cancel"作為它的名字,當(dāng)用戶按下取消按鈕,瀏覽器將一個叫做"_cancel"的參數(shù)放到請求中。AbstractWizardFormController接收到這個參數(shù),將控制權(quán)交給processCancel()方法。 默認(rèn)情況下此方法會拋出異常,表示取消操作是不被支持的,我們要覆寫這個方法,將用戶帶到你想讓他們點擊取消時看到的頁面。下面processCancel()方法的實現(xiàn)將用戶帶領(lǐng)到成功視圖。

          protected ModelAndView processCancel(HttpServletRequest   request,   HttpServletResponse   response, Object command,  BindException  bindException)  throws Exception {

               return new ModelAndView(getSucessView()) ;

          }

          每次驗證一個向?qū)П韱?/strong>:使用其他類型的命令控制器,命令對象只裝載一個次。但使用向?qū)Э刂破鳎脩裘客瓿上驅(qū)ы撁嬷械囊徊剑紩幸粋€命令對象設(shè)置進來。使用向?qū)В蛔鲆淮悟炞C是不可行的,太早的話,找到的驗證問題可能是由于用戶沒有完成向?qū)Ф鴮?dǎo)致的。太晚的話,在完成按鈕被按下后再做檢查就太遲了,因為發(fā)現(xiàn)的問題可能越過了多個頁面(用戶該回到哪個頁面呢?)   向?qū)Э刂破髟诿總€頁面驗證一次命令對象,不是只驗證一次。這是通過每次頁面跳轉(zhuǎn)時調(diào)用validatePage()方法實現(xiàn)的。validatePage()方法的默認(rèn)實現(xiàn)是空的(也就是沒有驗證),但是你可以覆寫這個方法,做出自己的判斷。 假設(shè)在motoristDetailForm頁面,詢問用戶的郵件地址,這個字段是可選的,但是如果輸入了值,必須輸入一個合法的E-mail地址。下面的validatePage()方法展示了用戶從motoristDetailForm頁面跳轉(zhuǎn)出來的時候如何驗證E-mail地址。

          protected void validatePage(Object  command,  Errors  errors, int page ) {

              Motorist  motorist =  (Motorist)  command;

              Motorist Validator  validator  = (MotoristValidator) getValidator();

              if(page == 0){

               validator.validateEmail(motorist.getEmail() , errors );

            }

          }  這里可以直接在validatePage()方法中檢查E-mail。然后,向?qū)б话阌泻脦醉椬侄涡枰炞C。如果這樣的話,validatePage()方法會變得很笨拙。我們建議你將驗證任務(wù)委托給控制器的驗證器對象的字段級驗證方法,就像我們在這里調(diào)用MotoristValidator的validateEmail()方法一樣。  這意味著當(dāng)你配置控制器的時候,你要設(shè)置validator屬性:

          <bean id="registerMotoristController" class="com.roadrantz.mvc.MotoristRegistrationController">

             <property name="rantService" ref="rantService" />

             <property name="pages">

                <list>

                  <value>motoristDetailForm</value>

                  <value>motoristVehicleForm</value>

                  <value>motoristConfirmation</value>

                  <value>redirect:home.htm</value>

                </list>

             </property>

             <property name="validator">

                <bean class="com.roadrantz.mvc.MotoristValidator" />

             </property>

          </bean>

          一個需要注意的重要事項是,不像其他命令控制器,向?qū)Э刂破鲝牟徽{(diào)用它們的驗證器對象的標(biāo)準(zhǔn)validate()方法。這是因為validate()方法驗證整個命令對象,然而在向?qū)е忻顚ο髮⒃诿總€頁面驗證一次。

        使用一次性控制器:一次性控制器比其他控制器簡單很多,ThrowawayController接口可以證明:

        public interface  ThrowawayController {

              ModelAndView execute() throws Exception ;

        }

        ThrowawayController接口和Controller接口不在同一個體系里。一次性控制器自己作為自己作為自己的命令對象,而不是通過一個HttpServletRequest或一個命令對象獲得參數(shù)。與WebWork Action相似,都是以同樣的方式工作。我們將RantsForController實現(xiàn)為ThrowawayController:

        public class RantsForDayController implements ThrowawayController{

           private Day day;

           public ModelAndView execute() throws Exception {

              List<Rant>  dayRants = rantService.getRantsForDay(day);

              return new ModelAndView("dayRants","rants",dayRants);

           }

           public void setDay(Date day){

           this.day = day;

           }

           private RantService rantSrvice;

        public void setRantSrvice(RantService rantSrvice) {

        this.rantSrvice = rantSrvice;

          }

        } 你必須在DispatcherServlet的上下文配置文件中聲明一次性控制器。只有一點很小的不同:

        <bean id="rantsForDayController" class="com.roadrantz.mvc.RantsForDayController" scope="prototype">

              <property name="rantService" ref="rantService">

        </bean> scope屬性已經(jīng)被設(shè)置為prototype。這就是一次性控制器獲取它們名字的地方。默認(rèn)情況下,所有的Bean都是Singleton。 但是因為ThrowawayContoller和Controller不處于同一繼承層次,DispatcherServlet不知道如何通知ThrowawayController。所以你必須告訴DispathcerServlet使用一種不同的 處理適配器。確切的說,你必須像下面這樣配置ThrowawayControllerHandlerAdapter:

          <bean id="throwawayHandler" class="org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter" />

        如果應(yīng)用程序既使用了常規(guī)控制器又使用了一次性控制器,你還應(yīng)該按如下方式聲明SimpleControllerHandleAdapter:

         <bean id="simpleHandler"  class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

        聲明兩處理器適配器使你可以在用一個應(yīng)用程序中混合使用兩種類型的控制器。

      處理異常:當(dāng)異常從控制器中跑出來時,SimpleMappingExceptionResolver負(fù)責(zé)營救。

      <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

         <property name="exceptionMappings">

             <props>

                 <prop key="java.lang.Exception">friendlyError</prop>

             </props>

         </property> 

      </bean>

         exceptionMappings屬性取得一個java.util.Properties,它映射了異常類名和邏輯視圖名。在本例中,基礎(chǔ)異常類被映射到邏輯名為friendlyError的視圖上,這樣如果有任何異常拋出的話,用戶不會在瀏覽器中看到一串晦澀的堆棧跟蹤信息。



                                                                                                           --    學(xué)海無涯
            

    主站蜘蛛池模板: 日本三级在线观看免费| 西西大胆无码视频免费| 亚洲邪恶天堂影院在线观看| 免费成人激情视频| 亚洲欧美在线x视频| 亚洲精品高清无码视频| 青青久在线视频免费观看| 黄色毛片免费观看| 久久久久久亚洲精品| 免费看的成人yellow视频| 好吊色永久免费视频大全| 亚洲欧洲日本精品| 亚洲精品国产va在线观看蜜芽| 久久国产精品成人免费| 中文字幕亚洲综合久久综合| 国产偷国产偷亚洲清高动态图| 国产成在线观看免费视频| 九一在线完整视频免费观看| 亚洲成aⅴ人片在线观| 中文字幕日韩亚洲| 一个人免费观看视频www| 两性色午夜免费视频| 亚洲欧美中文日韩视频| 亚洲av色福利天堂| 亚洲熟女乱综合一区二区| 久久经典免费视频| 99久久国产精品免费一区二区 | 成人免费在线视频| 久久久久免费精品国产| 国产亚洲情侣久久精品| 亚洲一级片在线播放| 亚洲人成人一区二区三区| 日韩免费视频网站| 一二三四影视在线看片免费 | 亚洲人配人种jizz| 亚洲AV永久无码精品| 亚洲av无码不卡私人影院| 成年女人午夜毛片免费视频| 亚洲精品免费视频| a级片免费观看视频| 深夜免费在线视频|